为何要了解复杂度分析
虽然说,代码写出来跑一遍结果可能更清晰直观,但受机器、数据量大小影响较大,且代码实现所需要使用的时间本身也是一种成本。如果通过复杂度分析,可以提前Pass掉一部分明显不合理的实现,可以有效提高我们的工作效率。
时间复杂度
分析时需要着重关注的点
1. 循环,通常是执行次数最多的地方;
2. 针对递归、嵌套循环,总复杂度是乘出来的;
3. 总复杂度等于量级最大的代码段的复杂度(当数据量到达一定程度时,低量级的可以忽略)。
常见的时间复杂度
O(1)
常量级。
通常代码中只要没有循环、递归操作,代码量再多也可以认为是常量级。
O(logn)
对数级。
如:在一个有序数组中二分查询某个数。
O(n)
线性级。
如:在数组中查找一个最大值。
O(nlogn)
线性对数级。
如:归并排序,快速排序。
O(n^k)
平方级、立方级、k次方级。
如:冒泡排序(平方级)等多层嵌套循环。
O(k^n)
指数级。
随着n的规模越大,算法执行时间会急剧增加,算法效率低下。
如:斐波那契额数列的低配版实现(无动态规划优化)。
O(n!)
阶乘级。
随着n的规模越大,算法执行时间会急剧增加,算法效率低下。
空间复杂度
分析方法和时间复杂度类似,区别在于空间复杂度关注的是代码执行时额外申请的内存空间。
常见的空间复杂度
O(1)、O(n)、O(n^2),比起时间复杂度的分析,要简单不少。
最好时间复杂度
最理想情况下,执行代码段的时间复杂度。
最坏时间复杂度
最糟糕情况下,执行代码段的时间复杂度。
平均时间复杂度
加权平均值,受具体算法和输入的影响,权重也会有所不同。
均摊时间复杂度
一种特殊的平均时间复杂度。如Java中支持动态扩容的ArrayList,平时Add时,时间复杂度是O(1),但在空间不足时进行Add,此时附加的扩容操作时间复杂度为O(n),但平摊下来其实整体时间复杂度仍为O(1)。
通常情况下,分析复杂度的时候,用一个复杂度就可以满足需求。除非碰到那种不同情况下,复杂度出现量级的差异,才会考虑几种复杂度都分析一遍。