时间复杂度学习笔记
时间复杂度学习笔记
一、大O渐进表示法规则
时间复杂度和空间复杂度一般都用大O的渐进表示法进行表示,规则如下:
- 所有常数都用常数1表示。
- 只保留最高阶项。
- 如果最高阶项存在且不是1,则去除与这个项的系数,得到的结果就是大O阶。
二、时间复杂度的定义
在计算机科学中,算法的时间复杂度是一个函数,它定量描述了该算法的运行时间。一个算法执行所耗费的时间,从理论上说,是不能算出来的,只有把你的程序放在机器上跑起来,才能知道。但是我们需要每个算法都上机测试吗?是可以都上机测试,但这很麻烦,所以才有了时间复杂度这个分析方式。
一个算法所花费的时间与其中语句的执行次数成正比,算法中的基本操作的执行次数,为算法的时间复杂度。
三、数学公式参考
3.1 等差数列
- 通项公式:aₙ = a₁ + (n-1)d (d 为公差)
- 前 n 项和:Sₙ = n(a₁ + aₙ) / 2
3.2 等比数列
- 通项公式:aₙ = a₁ · qⁿ⁻¹ (q 为公比)
- 前 n 项和:Sₙ = a₁(1 - qⁿ) / (1 - q) (q ≠ 1)
四、例题详解
例题 1:对数型 — 除法递减
代码:
i = n * n;
while (i != 1) {
i = i / 2;
}
分析过程:
-
初始值:i = n²
-
循环规律:每次循环 i 除以 2,即 i = n²/2, n²/4, n²/8, …
-
设循环执行了 k 次后 i = 1,则:
n² / 2ᵏ = 1 → 2ᵏ = n² → k = log₂(n²) = 2·log₂n
-
根据大O规则(去除系数),时间复杂度为:O(log n)
例题 2:对数型 — 乘法递增
代码:
int i = 1;
while (i < n) {
i = i * 2;
}
分析过程:
-
初始值:i = 1
-
循环规律:每次循环 i 乘以 2,即 i = 1, 2, 4, 8, …
-
设循环执行了 k 次后退出,则:
2ᵏ ≥ n → k = log₂n
-
时间复杂度为:O(log n)
例题 3:平方根型 — 二次增长判断
代码:
x = 0;
while (n >= (x + 1) * (x + 1)) {
x = x + 1;
}
分析过程:
-
初始值:x = 0
-
循环规律:每次循环 x 加 1
-
设循环执行了 k 次后退出,则退出条件为 (x+1)² ≥ n,即:
(x + 1)² ≈ n → x + 1 = √n → x = √n - 1
-
循环执行了 √n - 1 次,时间复杂度为:O(√n)
例题 4:嵌套循环 — 多项式型
代码:
int m = 0;
for (i = 1; i < n; i++) {
for (j = 1; j <= 2 * n; j++) {
m++;
}
}
分析过程:
-
外层循环:i 从 1 到 n-1,执行 n-1 次
-
内层循环:j 从 1 到 2n,每次执行 2n 次
-
基本操作 m++ 的总执行次数:
(n - 1) × 2n = 2n² - 2n
-
根据大O规则(只保留最高阶项 2n²,去除系数 2),时间复杂度为:O(n²)
例题 5:嵌套循环 — 等比数列求和
代码:
int k = 1;
while (k < n) {
for (int i = 0; i < k; i++) {
操作;
}
k *= 2;
}
分析过程:
-
外层 while 循环:k 取值 1, 2, 4, 8, …, 直到 k ≥ n
-
内层 for 循环:分别执行 1, 2, 4, 8, … 次
-
设 k 最大取值为 2ᵐ(2ᵐ < n),则总执行次数为等比数列求和:
S = 1 + 2 + 4 + 8 + … + 2ᵐ = 2ᵐ⁺¹ - 1
-
由于 2ᵐ < n,所以 2ᵐ⁺¹ < 2n,因此 S < 2n - 1
-
根据大O规则,时间复杂度为:O(n)
例题 6:递归函数 — 指数型(斐波那契类)
代码:
public int a(int n) {
if (n <= 0) {
return 0;
} else if (n == 1) {
return 1;
} else if (n == 2) {
return 2;
} else {
return a(n - 1) + a(n - 2);
}
}
分析过程:
-
递归调用树展开:
- a(n) → a(n-1) + a(n-2)
- a(n-1) → a(n-2) + a(n-3)
- a(n-2) → a(n-3) + a(n-4)
- …
-
递归树是一棵二叉树,深度为 n,每层节点数约 2ᵏ(k 为层数)
-
总节点数约为 2ⁿ - 1
-
时间复杂度为:O(2ⁿ)
五、总结对照表
| 例题 | 代码特征 | 时间复杂度 | 分析方法 |
|---|---|---|---|
| 例题 1 | i = n²; i = i / 2 | O(log n) | 对数方程求解 |
| 例题 2 | i = 1; i = i * 2 | O(log n) | 对数方程求解 |
| 例题 3 | x = 0; (x+1)² ≤ n | O(√n) | 二次方程求解 |
| 例题 4 | 双层 for 循环,内层与 n 成正比 | O(n²) | 乘法原理 |
| 例题 5 | while + for,k 每次翻倍 | O(n) | 等比数列求和 |
| 例题 6 | 递归 a(n) = a(n-1) + a(n-2) | O(2ⁿ) | 递归树分析 |
六、核心技巧归纳
- 除法/乘法递增:若变量每次除以或乘以常数 c(c > 1),则循环次数为 O(log n)。
- 嵌套循环:外层循环次数 × 内层循环次数 = 总执行次数。
- 等比数列求和:若内层循环次数呈等比数列增长,用等比数列求和公式化简。
- 递归函数:画出递归调用树,按二叉树节点数估算,通常为 O(2ⁿ) 量级。
- 大O简化三步走:去常数 → 留最高阶 → 去系数。
七、常见时间复杂度速查
| 量级 | 名称 | 典型算法 |
|---|---|---|
| O(1) | 常数阶 | 哈希表查找、数组下标访问 |
| O(log n) | 对数阶 | 二分查找、二叉搜索树操作 |
| O(√n) | 平方根阶 | 试除法判断素数 |
| O(n) | 线性阶 | 线性查找、遍历数组 |
| O(n log n) | 线性对数阶 | 归并排序、快速排序(平均) |
| O(n²) | 平方阶 | 冒泡排序、选择排序、插入排序 |
| O(2ⁿ) | 指数阶 | 斐波那契递归、子集枚举 |
| O(n!) | 阶乘阶 | 旅行商问题(暴力枚举) |