不同复杂度的算法简介


算法复杂度的表示:大O

算法复杂度不能用运行秒数来衡量,因为这是计算机速度、编译器速度相关的。应该以算法运行步数为基准。而如果算法步数是一个多项式,那么只取多项式里增长幅度最大的一项,然后这项的系数要省略,因为对于极大的输入来说,这是最关键的决定速度的因素。

常数复杂度的算法

常数复杂度就是指该算法的运行时间跟输入规模无关。不管输入大还是小,运行时间都是一定的。这种算法很少见,举例来说有 len(list),2 个浮点数相乘等。

对数复杂度的算法

该算法的运行时间是呈对数级增加的,并且运行时间与至少其中一个输入有关。二分查找的运行时间就是对数级的。还有,对数的底不重要,因为根据公式,对数的底实质上就是一个常数系数。

举个例子:

def intToStr(i):
    digit = '0123456789'
    if i == 0:
        return '0'
    result = ''
    while i > 0:
        result = digit[i%10] + result
        i = i // 10
        return result

我们可以看到,循环决定了这个算法的时间复杂度。我们可以算这个循环运行了多少次,这个循环运行次数等于 i 的长度,因为当 i 增长时,长度增长大概是 log(i) 的,所以 intTostr 的复杂度为 O(log(i))。

线性复杂度的算法

处理list的时间常常是线性的,因为我们经常需要接触 list 的每个元素。

def addDigits(s):
    val = 0
    for c in s:
        val += int(c)
        return val

还有并不是处理list时也可能有线性复杂度:

def factorial(x):
    if x == 1:
        return 1
    else:
        return x*factorial(x - 1)

该算法的空间复杂度也是 O(x)。因为递归每进行一次都会分配一个新的栈给它,而这个栈会一直被占用直到调用返回。

对数线性复杂度的算法

这个复杂度是 2 个单位的相乘,每个都跟输入的规模有关。最常用的对数线性算法是归并排序,是 O(nlog(n)) 的复杂度,其中n为排序 list 的长度。

多项式复杂度的算法

最常见的多项式复杂度算法是 2 次方复杂度算法,它的复杂度是输入 n 的平方。

def isSubset(L1, L2):
    for e1 in L1:
    matched = False
    for e2 in L2:
        if e1 == e2:
            matched = True
            break
    if not matched:
        return false
    return True

内循环运行了 O(len(L2)) 次,外循环运行了 O(len(L1)) 次。所以总体时间复杂度为 O(len(L1)*len(L2))。

指数复杂度的算法

这个超纲了,就不介绍了。:smile::smile: