复杂度分析
复杂度分析是整个算法学习的精髓,只要掌握了它,数据结构和算法的内容基本上就掌握了一半。
事后统计法
把代码跑一遍,通过统计、监控,就能得到算法执行的时间和占用内存的大小。这样的话有很大的局限性。
1. 测试结果非常依赖测试环境
2. 测试结果受数据规模的影响很大
我们需要一个不用具体的测试数据来测试,就可以粗略地估计算法的执行效率的方法。
大O复杂度表示法
大O时间复杂度实际行并不具体表示代码真正的执行时间,而是表示代码执行时间随数据规模增长的变化趋势,所以也叫做渐进时间复杂度,简称时间复杂度。
时间复杂度
如何进行时间复杂度分析
1. 只关注循环执行次数最多的一段代码,最耗时的那一部分。
2. 加法法则:总复杂度等于量级最大的那段代码的时间复杂度。如果有两段代码在一起执行,时间复杂度分别为O(n)和O(n^2),那么总的时间复杂度为O(n^2)
3. 乘法法则:嵌套代码的时间复杂度等于嵌套内外代码复杂度的乘积。假设每次for循环的的时间复杂度为O(n),那么嵌套一次也就是两层for循环为O(n^2),三层为O(n^3)。
常见的时间复杂度分析
多项式量级:常量级O(1)、对数级O(logn)、线性级O(n)、线性对数级O(nlogn)、平方级O(n^2) O(n^3) .....O(n^k)
非多项式量级:指数级O(2^n)、阶乘级O(n^k)
O(1):表示常量级的时间复杂度,并不是指只执行一行代码。一般情况下没有循环和递归,时间复杂度就为O(1)
O(logn)、O(nlogn):
1 int i = 0; 2 while(i <= n) 3 { 4 i = i *2; 5 }
分析上述代码的时间复杂度,可以看出 i 的取值就是一个等比数列,2^0 * 2^1 * 2^2 * ......2^k*....2^x = n。 x = logn,所以这段代码的时间复杂度为就是O(logn)。
那么O(nlogn)也就好理解了,在外面嵌套一个O(n),可以看一下上边说的乘法法则。
O(m+n)、O(m*n):在我们没办法评估两个代码段谁的量级大的时候,就不能利用加法法则,省略掉其中的一个,所以会出现O(m+n)
空间复杂度
空间复杂度就是表示存储空间与数据规模之间的增长关系。简单说就是你申请空间与n的关系。没有关系就是O(1).常见的空间复杂度有 O(1)、O(n)、O(n^2).
四个时间复杂度分析知识点,最好时间复杂度(best case time complesity)、最坏时间复杂度(average case time complexity)、均摊时间复杂度(amortized time complexity)
最好时间复杂度就是在最理想的情况下,执行这段代码的时间复杂度。
最坏时间复杂度就是在最糟糕的情况下,执行这段代码的时间复杂度。
平均时间复杂度就是将每个操作所用的时间复杂度之和除以一共操作了多少次。当然每个操作所出现的概率是不一样的。所以平均时间复杂度就是加权平均时间复杂度或者期望时间复杂度。
均摊时间复杂度对应的分析方法为摊还分析(或者叫平摊分析)。大致思想:每一次O(n)的插入操作,都会跟着n-1次O(1)的插入操作,所以把耗时多的那次操作均摊到接下来的n-1次耗时少的操作上,均摊下来,这一组连续操作的均摊时间复杂度就是O(1).应用场景:对一个数据结构进行一组连续操作中,大部分情况下时间复杂度都很低,只有个别的情况下时间复杂度比较高,而且这些操作之间前后连贯的时序关系,这个时候就可以将这一组操作放在一块分析。