一、什么是算法的时间复杂度
(一)引出算法
什么是算法?加入有下面一道题:
如果 a+b+c=1000,且 a^2+b^2=c^2(a,b,c 为自然数),如何求出所有a、b、c可能的组合?
刚开始的想法可能就是将a、b、c可能的值都列出来然后进行组合:
- a、b、c的可能取值范围是[0,1000]
- 然后根据枚举进行条件组合
import time def outter(func): def innder(*args, **kwargs): start = time.time() func() end = time.time() print('运行时间:%s' % (end - start)) return innder @outter def find(): for i in range(0, 1001): for j in range(0, 1001): for k in range(0, 1001): if i + j + k == 1000 and i ** 2 + j ** 2 == k ** 2: print(i, j, k) find()
输出的结果如下:
0 500 500
200 375 425
375 200 425
500 0 500
运行时间:182.12541675567627
显然,这样会耗费非常多的时间,如果换一种思路,当a、b确认后c是不是也就可以通过1000-a-b进行确认了呢?进行如下优化:
import time def outter(func): def innder(*args, **kwargs): start = time.time() func() end = time.time() print('运行时间:%s' % (end - start)) return innder @outter def find(): for i in range(0, 1001): for j in range(0, 1001): k = 1000 - i - j if i ** 2 + j ** 2 == k ** 2: print(i, j, k)
此时的输出为:
0 500 500
200 375 425
375 200 425
500 0 500
运行时间:1.328075885772705
可以看到运行的时间从182.12541675567627s减少到1.328075885772705s,进行很大空间的优化。
我们通过不同的方法和思想,达到不一样的效果,这就是一种算法,算法是独立存在的一种解决问题的方法和思想。
(二)时间复杂度
1、什么是时间复杂度
上述的两种算法,显然第二种更优,但是你是如何衡量算法的效率呢?仅仅通过时间来进行判断吗?如果仅仅通过时间来判断显然是不合适的,因为如果第二种算法跑在一个硬件设备配置很低的机器,可能耗费的时间更长。
虽然从时间上不能衡量,但是一个算法有多少基本操作是确定的,多少个基本操作就代表会花费多少时间单位,这样就可以忽视机器环境的影响。对于算法效率的衡量可以使用“大O记法”:
对于单调的整数函数f,如果存在一个整数函数g和实常数c>0,使得对于充分大的n总有f(n)<=c*g(n),就说函数g是f的一个渐近函数(忽略常数),记为f(n)=O(g(n))。也就是说,在趋向无穷的极限意义下,函数f的增长速度受到函数g的约束,亦即函数f与函数g的特征相似。
对于时间复杂度就是:
假设存在函数g,使得算法A处理规模为n的问题示例所用时间为T(n)=O(g(n)),则称O(g(n))为算法A的渐近时间复杂度,简称时间复杂度,记为T(n)
2、如何计算时间复杂度
- 基本操作,即只有常数项,认为其时间复杂度为O(1)
- 顺序结构,时间复杂度按加法进行计算
- 循环结构,时间复杂度按乘法进行计算
- 分支结构,时间复杂度取最大值
判断一个算法的效率时,往往只需要关注操作数量的最高次项,其它次要项和常数项可以忽略;在没有特殊说明时,我们所分析的算法的时间复杂度都是指最坏时间复杂度
比如上面算法1抽象的时间复杂度:
T(n) = n * n * n * max(1,0) = n^3*1 = n^3 = O(n^3)
对于上面算法2抽象的时间复杂度:
T(n) = n*n*(1+max(1,0)) = n^2 * (1+1) = 2*n^2 = n^2 =O(n^2)
由此可见第2中算法更优。
3、常见时间复杂度
次数函数 | 阶数 | 术语 |
3 | O(1) | 常数阶 |
2n+3 | O(n) | 线性阶 |
2n2+3n+1 | O(n2) | 平方阶 |
5log2n+10 |
O(logn) | 对数阶 |
3n+3nlog2n+20 | O(nlogn) | nlogn阶 |
5n3+2n2+3n+4 | O(n3) | 立方阶 |
2n | O(2n) | 指数阶 |
上述时间复杂度所消耗的时间从小到大:
O(1) < O(logn) < O(n) < O(nlogn) < O(n2
) < O(n3
) < O(2n) < O(n!) < O(nn
)
二、Python中list和dict方法的时间复杂度
1、list内置方法时间复杂度
注意:上述的时间复杂度是最坏时间复杂度
2、dict内置方法时间复杂度
注意:上述的时间复杂度是最坏时间复杂度