一般来说,解决问题的方法不止一种,因此我们需要通过比较不同算法的性能,选择出最佳的算法。一个算法的好坏,我们可以从时间和空间两个维度去衡量,并且分为两个阶段,一是 「算法完成前的理论分析」,这时假设除算法之外的其他因素对算法的实现是没有影响的,时间、空间复杂度就属于理论分析阶段,二是 「算法完成后的实际分析」,这时算法消耗的实际时间与计算机的硬件水平相关。
一、时间、空间复杂度说明
1. 时间复杂度
一个算法花费的时间与算法中语句的执行次数成正比。一个算法中所有语句的总执行次数称为 「时间频度或语句频度」,记为T(n),其中n称为 「问题的规模」 。
当n不断变化时,它所呈现出的规律称之为时间复杂度。算法的时间复杂度反映了 「程序执行时间随输入规模增长而增长的量级 」,它只关注最高数量级,且与之系数也没有关系,例如,T(n)=n^2 和 T(n)=5n^2 + n虽然算法的时间频度不一样,但他们的时间复杂度却是一样的。通常情况来说,比起空间复杂度,我们更加注重时间复杂度。
2. 空间复杂度
空间复杂度是 「表示算法的存储空间与数据规模之间的增长关系」。判断如下:
- 算法中是否使用了额外空间来存储数据;
- 这个额外空间的大小会不会随着
n的变化而变化。
通常来说,只要算法不涉及 「动态分配」 的空间,以及 「递归、栈」 所需的空间,空间复杂度通常为 O(1)。一个一维数组a[n],随输入规模n的增大而增大,那么其空间复杂度为O(n),二维数组空间复杂度则为O(n^2)。
3. 常见的复杂度
将常见的时间、空间复杂度按数量级递增排序为:
-
常数阶O(1):哈希表的查找(以空间换时间)
-
对数阶O(logn) :二分查找
-
线性阶O(n)
-
线性对数阶O(nlogn)
「若一个算法的复杂度属于上面其中的一种,那么这个算法的时间效率较高」
-
平方阶O(n^2)
-
立方阶O(n^3)
-
指数阶O(2^n)
-
阶乘O(n!)
二、时间、空间复杂度的计算
复杂度的计算步骤如下:
(1)找出算法中的 「基本语句」;
基本语句就是算法中执行次数最多的那条语句, 通常为最内层循环的循环体。
(2)计算基本语句的执行次数的 「数量级」;
忽略所有低次幂和最高次幂的系数,只需关注基本语句执行次数的数量级。
(3)用大O记号表示算法的时间性能。
1. 递归复杂度
T(n) 的递推关系式有 T(n) = a * T(b / n) * f(n)。其中 n 表示问题规模, a 为递推的子问题数量, b/n 表示每个子问题的规模(假设每个子问题的规模基本一样),f(n) 为递推以外的计算工作。
O(n) 的计算方式:
2. 常见场景的复杂度
- 时间复杂度 O(n^2)
- 时间复杂度 O(logn)
var i = 1;
while (i <= n) {
i = i * 2;
}
-
时间复杂度 O(nlogn)
-
空间复杂度 O(n)
function fun(n) {
var temp = [];
for (let i = 0; i < n; i++) {
temp.push(i)
}
}
参考blog: