算法时间复杂度和空间复杂度分析
本文用大 O 表示法来表示时间复杂度和空间复杂度,大 O 并不代表代码真正执行时的复杂度,而是代表着一种变化趋势,所以在分析时,我们就可以忽略公式中的低阶、常量、系数。(通俗点说,我们只考虑执行次数最多的代码:同一层的代码,取其中量级更大的,嵌套的代码, 其复杂度为嵌套内外代码复杂度的乘积)
时间复杂度
常用时间复杂度
- O(1) 常量阶
- O(logn) 对数阶
- O(n) 线性阶
- O(nlogn) 线性对数阶
- O(n2) 平方阶 O(n3) 立方阶 … O(nk) k次方阶
- O(2n) 指数阶
- O(n!) 阶乘阶
O(1) 常量阶
O(1) 并不代表只执行一行代码,而是代表着代码执行次数不会随着 n 的变大而变大,是一个常量次数的执行。
int i = 1;
int j = 2;
System.out.println(i * j);
O(logn) 对数阶 & O(nlogn) 线性对数阶
对数时间复杂度是最难分析的一种时间复杂度,因为其并不像其他时间复杂度那样直观。
for(int i = 1; i <= n; i *= 2) {
System.out.println(i);
}
上面的代码时间复杂度是 O(log2n),在采用大 O 计数法的时候,可以忽略系数,所以上面的时间复杂度我们认为是 O(logn)。
了解了 O(logn),O(nlogn) 就比较容易理解了。如果将上面的代码循环执行 n 次,就是 O(nlogn) 了。
for (int i = 0; i < n; i++) {
for(int j = 1; j <= n; j *= 2) {
System.out.println("i: " + i + ", j: " + j);
}
}
O(n) 线性阶
for (int i = 0; i < n; i++) {
System.out.println(i);
}
O(n2) 平方阶 & O(n3) 立方阶
for (int i = 0; i < n; i++) {
for(int j = 0; j < n; j++) {
System.out.println("i: " + i + ", j: " + j);
}
}
for (int i = 0; i < n; i++) {
for(int j = 0; j < n; j++) {
for(int k = 0; k < n; k++) {
System.out.println("i: " + i + ", j: " + j + ", k: " + k);
}
}
}
O(2n) 指数阶
for(int i = 0; i < Math.pow(2, n); i++) {
System.out.println(i);
}
O(n!) 阶乘阶
public void f(int n) {
if(n == 0) {
return;
}
for(int i = 0; i < n; i++) {
f(n - 1);
}
}
空间复杂度
时间复杂度代表着算法的执行时间和数据规模大小的关系,而空间复杂度代表着算法的存储空间和数据规模大小的关系。
常用空间复杂度
- O(1) 常量阶
- O(n) 线性阶
- O(n2) 平方阶
空间复杂度的分析比时间复杂度要简单,我们需要分析的是,在代码执行过程中,需要额外申请的内存空间的大小和 n 之间的关系。
总结
分析时间复杂度和空间复杂度是学习算法必不可少的一步,需要勤加练习,最后附上一张图(代码撸多了就会发现,几乎所有的数据结构和算法都跑不出这几个)
参考:《数据结构与算法之美》