本文旨在分析时间复杂度与空间复杂度,以及时间复杂度的相关概念。
时间复杂度
概述
时间复杂度的全称是渐进时间复杂度,表示算法的执行时间与数据规模之间的增长关系。
大O复杂度表示法
大 O 时间复杂度实际上并不具体表示代码真正的执行时间,而是表示代码执行时间随数据规模增长的变化趋势,所以,也叫作渐进时间复杂度(asymptotic time complexity),简称时间复杂度。
1 | T(n) = O(f(n)) |
时间复杂度分析原则
原则一:只关注循环体
在分析一个算法、一段代码的时间复杂度的时候,只关注循环执行次数最多的那一段代码即可。
原则二:加法法则
概念:总复杂度等于量级最大的那段代码的复杂度。
公式:如果 T1(n) = O(f(n)),T2(n) = O(g(n));那么 T(n) = T1(n)+T2(n) = max(O(f(n)), O(g(n))) = O(max(f(n), g(n)))。
原则三:乘法法则
概念:嵌套代码的复杂度等于嵌套内外代码复杂度的乘积。
公式:如果 T1(n) = O(f(n)),T2(n) = O(g(n));那么 T(n) = T1(n)*T2(n) = O(f(n))*O(g(n)) = O(f(n)*g(n))。
场景:
嵌套循环:假设在方法call中循环调用方法f,call的时间复杂度为 T1,f的时间复杂度为 T2,则总的时间复杂度为 T(n) = T1(n) * T2(n) = O(n*n) = O(n2)。
1 | int cal(int n) { |
常见的时间复杂度


分类
- 多项式量级:
O(1),O(logn),O(n),O(n),O(n^k) - 非多项式量级:
O(2n)和O(n!)
O(1)
一般情况下,只要算法中不存在循环语句、递归语句,即使有成千上万行的代码,其时间复杂度也是Ο(1)。
O(logn)、O(nlogn)
对数阶时间复杂度是非常常见的算法时间复杂度。比如,归并排序、快速排序的时间复杂度都是 O(nlogn)。
O(m+n)、O(m*n)
假设 m 和 n 表示两个数据规模,且无法先评估量级大小,则不能忽略任意一个。对应的程序加法法则为 T1(m) + T2(n) = O(f(m) + g(n)),乘法法则为T1(m)*T2(n) = O(f(m) * f(n))。
相关概念
同一段代码,在不同输入的情况下,复杂度量级有可能是不一样的。因此,引入了几个时间复杂度的相关概念。其中包括:最好情况时间复杂度(best case time complexity)、最坏情况时间复杂度(worst case time complexity)、平均情况时间复杂度(average case time complexity)、均摊时间复杂度(amortized time complexity)。
例子:模拟一个查找函数 find,遍历需要匹配的数组,若元素存在则返回,若不存在则返回 -1 。
1 | // n表示数组array的长度 |
最好/最坏情况时间复杂度
最好情况时间复杂度就是,在最理想的情况下,执行这段代码的时间复杂度。最坏情况时间复杂度就是,在最糟糕的情况下,执行这段代码的时间复杂度。
如例子中,如果查找元素刚好在数组首位,则立即匹配到,最好时间复杂度是 O(1);若元素不在数组中,则需要遍历整个数组,即最坏时间复杂度是 O(n)。
平均情况时间复杂度
最好情况时间复杂度和最坏情况时间复杂度对应的都是极端情况下的代码复杂度,发生的概率其实并不大。为了更好地表示平均情况下的复杂度,需要引入概念:平均情况时间复杂度,简称平均时间复杂度。
如例子中,假设要查找元素在数组中与不在数组中的概率均为 1/2,元素在数组中任意位置出现的概率均为 1/n。则要查找的数据出现在任意位置,就是 1/2n,不出现的概率为 1/2,所以加权平均和为1*1/2n+2*1/2n+...+n*1/2n+n*1/2 = (3n+1)/4,最终时间复杂度为 O(n),即平均时间复杂度为 O(n)。
均摊时间复杂度
对一个数据结构进行一组连续操作中,大部分情况下时间复杂度都很低,只有个别情况下时间复杂度比较高,而且这些操作之间存在前后连贯的时序关系。此时,可以将这一组操作放在一块儿分析,看是否能将较高时间复杂度那次操作的耗时,平摊到其他那些时间复杂度比较低的操作上。而且,在能够应用均摊时间复杂度分析的场合,一般均摊时间复杂度就等于最好情况时间复杂度。
例子:模拟一个元素插入数组函数insert,若数组满了,则将所有值相加合并到数组首位,否则往数组末端插入元素。
1 | // array表示一个长度为n的数组 |
可以看到,在数组未满的情况下,插入的时间复杂度为 O(1),而数组满的情况下,插入的时间复杂度为 O(n)。由于所有情况出现的概率均为 1/(n+1),则加权平均值为 1*1/(n+1)+1*1/(n+1)+...+1*1/(n+1)+n*1/(n+1) = 2n/(n+1)*,即平均时间复杂度为 O(1)。
而摊还分析法的思路,就是:每一次 O(n) 的插入操作,都会跟着 n-1 次 O(1) 的插入操作,所以把耗时多的那次操作均摊到接下来的 n-1 次耗时少的操作上,均摊下来,这一组连续的操作的均摊时间复杂度就是 O(1),得到的时间复杂度就是均摊时间复杂度。因此,均摊时间复杂度其实是特殊的平均时间复杂度。
空间复杂度
概述
空间复杂度全称是渐进空间复杂度(asymptotic space complexity),表示算法的存储空间与数据规模之间的增长关系。