基本概念
1. 时间复杂度(Time Complexity)
- 衡量算法运行时间随输入规模增长的变化趋势。
- 不关注具体运行时间(如秒),而是关注操作次数的增长率。
- 使用大O记号(Big O Notation) 表示上界。
2. 空间复杂度(Space Complexity)
- 衡量算法所需内存空间随输入规模增长的变化趋势。
- 包括:
- 输入数据占用的空间(通常不计入)
- 辅助空间(算法额外使用的变量、递归栈、临时数组等)
常见的复杂度类型
| 复杂度 | 名称 | 增长速度 |
|---|---|---|
| O(1) | 常数时间 | 最快,与 n 无关 |
| O(log n) | 对数时间 | 非常高效(如二分查找) |
| O(n) | 线性时间 | 常见于单层循环 |
| O(n log n) | 线性对数 | 高效排序算法(如归并、快排平均) |
| O(n²) | 平方时间 | 双重循环,小规模可用 |
| O(2ⁿ) | 指数时间 | 组合爆炸,仅适用于极小 n |
| O(n!) | 阶乘时间 | 极慢(如暴力解旅行商问题) |
分辨方式
第1步:确定输入规模 n
- 找出影响运行时间的主要变量。
- 常见情况:
- 数组长度 →
n = len(arr) - 字符串长度 →
n = len(s) - 矩阵大小 →
n = rows × cols或分别用m, n - 树的节点数 →
n = number of nodes
- 数组长度 →
📌 注意 :如果有多个变量(如两个数组长度分别为
m和n),保留两者,不要强行合并。
第2步:找出"基本操作"并数执行次数
基本操作通常是:
- 比较(
if a > b) - 赋值(
x = y) - 算术运算(
i + 1) - 函数调用(若内部复杂度已知)
重点看循环和递归!
情况1:单层循环
python
for i in range(n):
print(i)
→ 循环体执行 n 次 → O(n)
情况2:嵌套循环
python
for i in range(n):
for j in range(n):
do_something() # O(1)
→ 内层执行 n 次,外层也 n 次 → 总共 n × n = n² → O(n²)
🔍 变种:内层依赖外层
python
for i in range(n):
for j in range(i): # j 从 0 到 i-1
...
总次数 = 0 + 1 + 2 + ... + (n−1) = n(n−1)/2 ≈ n²/2 → O(n²)(忽略常数)
情况3:对数循环(每次减半)
python
while n > 1:
n //= 2
→ 循环次数 = log₂n → O(log n)
情况4:多个独立循环
python
for i in range(n): ... # O(n)
for i in range(n): ... # O(n)
总时间 = O(n) + O(n) = O(n)(加法取最大项)
第3步:处理递归
写递推式,再估算。
方法A:展开法(适合简单递归)
python
def f(n):
if n <= 1: return 1
return f(n-1) + 1
- T(n) = T(n−1) + O(1)
- 展开:T(n) = T(n−2) + 2 = ... = T(0) + n → O(n)
方法B:主定理(Master Theorem)------用于分治
适用于形如:
T(n) = a·T(n/b) + f(n)
常见例子:
- 归并排序:T(n) = 2T(n/2) + O(n) → O(n log n)
- 二分查找:T(n) = T(n/2) + O(1) → O(log n)
💡 主定理速记:
- 若 f(n) = O(n^c),比较 c 与 log_b(a)
- c < log_b(a) → T(n) = O(n^{log_b a})
- c = log_b(a) → T(n) = O(n^c log n)
- c > log_b(a) → T(n) = O(f(n))
第4步:简化表达式(大O规则)
- 去掉常数:5n → n
- 去掉低阶项:n² + n + 100 → n²
- 多变量保留:O(m + n)、O(mn)
估算空间复杂度的步骤
第1步:区分"输入空间"和"额外空间"
- 输入数组、字符串等不算在空间复杂度中。
- 只算算法自己申请的内存。
第2步:检查以下来源
| 来源 | 是否计入 | 示例 |
|---|---|---|
| 局部变量 | 是(但通常 O(1)) | int x = 0 |
| 新建数组/哈希表 | 是 | seen = [False]*n → O(n) |
| 递归调用栈 | 是 | 递归深度 d → O(d) |
| 返回结果 | 通常不算(除非题目特别说明) | LeetCode 中返回数组一般不计入 |
第3步:典型场景
- 迭代算法(无递归、无额外结构)→ O(1)
- 使用哈希表记录元素 → O(n)
- 递归深度为 n(如链表递归)→ O(n)
- 平衡树递归(如BST)→ O(log n)
快速估算口诀(面试可用)
| 结构 | 时间复杂度 | 空间复杂度 |
|---|---|---|
| 单层 for/while | O(n) | O(1) |
| 双重嵌套循环 | O(n²) | O(1) |
| 二分查找 | O(log n) | O(1)(迭代)或 O(log n)(递归) |
| 归并/快排 | O(n log n) | O(n)(归并)或 O(log n)(快排平均) |
| DFS/BFS(图/树) | O(V + E) | O(V)(栈/队列+visited) |
| 动态规划(一维) | O(n) | O(n) 或 O(1)(滚动数组) |