算法复杂度速查手册
本文是 OI Wiki - 复杂度简介 的整合精简版,所有示例均使用 C++。
1. 核心概念
| 概念 | 说明 |
|---|---|
| 基本操作 | 加减乘除、变量访问、赋值等,每次算一步 |
| 时间复杂度 | 算法执行步骤数随输入规模 增长的趋势 |
| 空间复杂度 | 算法占用内存随输入规模 增长的趋势 |
| 最坏复杂度 | 所有可能输入中,用时最长的情况(竞赛中默认用这个) |
| 平均复杂度 | 所有可能输入下用时的平均值 |
为什么关注"趋势"而非"精确值"?
现代 CPU 每秒处理数亿次操作。
的算法在 时需要 次操作,直接超时;
只需 次,轻松通过。趋势比常数系数更重要。
2. 渐近符号(大 O 家族)
| 符号 | 类比 | 含义 | 何时用 |
|---|---|---|---|
| 上界 且 下界(紧确界) | 精确描述增长趋势 | ||
| 渐近上界 | 描述最坏情况(最常用) | ||
| 渐近下界 | 证明算法不可能更快 | ||
| 严格渐近上界 | 数学分析(竞赛少用) | ||
| 严格渐近下界 | 数学分析(竞赛少用) |
记忆口诀:含"等于"用大写,不含等于用小写;相等是 ,小于是 ,大于是 。
常用性质
f₁(n) + f₂(n) = O(max(f₁, f₂)) // 取大的那个
f₁(n) × f₂(n) = O(f₁ × f₂) // 嵌套相乘
log_a(n) = O(log n) // 对数底数无所谓,可省略
f(n) = Θ(g(n)) ⟺ f = O(g) 且 f = Ω(g)
3. 复杂度计算实例
3.1 嵌套循环 → 相乘
int n, m;
std::cin >> n >> m;
for (int i = 0; i < n; ++i) { // n 次
for (int j = 0; j < n; ++j) { // n 次
for (int k = 0; k < m; ++k) // m 次
std::cout << "hello\n";
}
}
// 复杂度:Θ(n²m)
规则:嵌套循环各层次数相乘。
3.2 图的 DFS / BFS → 点 + 边
// 对 n 个点、m 条边的图做 DFS
void dfs(int u) {
visited[u] = true;
for (int v : graph[u]) // 每条边只走一次
if (!visited[v]) dfs(v);
}
// 复杂度:Θ(n + m)
规则:每个节点和每条边各访问常数次 → 。
3.3 常量循环 → O(1)
constexpr int N = 100000; // N 是固定常量,与输入无关
for (int i = 0; i < N; ++i)
std::cout << "hello\n";
// 复杂度:O(1) —— N 不随输入变化,视为常数
关键:判断一个变量是否是"输入规模"。与输入无关的量都是常量,当 1 处理。
4. 主定理(Master Theorem)
用于快速求递归算法的复杂度。
适用形式:,其中 。
| 情况 | 条件 | 结论 |
|---|---|---|
| 情况 1 | , | |
| 情况 2 | ,且满足正则条件 | |
| 情况 3 | , |
正则条件(情况 2 需满足):,对某常数 且足够大的 成立。
速查示例
| 递推式 | 适用情况 | 结论 | |||
|---|---|---|---|---|---|
| 2 | 2 | 1 | 情况 1 | ||
| 1 | 2 | 0 | 情况 2 | ||
| 1 | 2 | 0 | 情况 3, | ||
| 1 | 2 | 0 | 情况 3, |
对应算法举例
// 归并排序:T(n) = 2T(n/2) + n → Θ(n log n) [情况 3, k=0]
void mergeSort(int* arr, int l, int r) {
if (l >= r) return;
int mid = (l + r) / 2;
mergeSort(arr, l, mid); // T(n/2)
mergeSort(arr, mid+1, r); // T(n/2)
merge(arr, l, mid, r); // O(n)
}
// 二分查找:T(n) = T(n/2) + 1 → Θ(log n) [情况 3, k=0]
int binarySearch(int* arr, int l, int r, int target) {
if (l > r) return -1;
int mid = (l + r) / 2;
if (arr[mid] == target) return mid;
if (arr[mid] < target) return binarySearch(arr, mid+1, r, target);
return binarySearch(arr, l, mid-1, target);
}
5. 常见复杂度对比
从快到慢排列( 时的量级参考):
| 复杂度 | 名称 | 时操作数 | 典型算法 |
|---|---|---|---|
| 常数 | 1 | 哈希表查找 | |
| 对数 | 二分查找 | ||
| 平方根 | 分块、质因数分解 | ||
| 线性 | 线性扫描、DFS/BFS | ||
| 线性对数 | 归并排序、堆排序 | ||
| 平方 | ❌ | 冒泡排序、朴素 DP | |
| 指数 | — | 暴力枚举子集 |
竞赛经验: 次操作约耗时 1 秒。 时, 通常安全; 时才能用 。
6. 快速判断方法
循环嵌套? → 各层次数相乘
顺序执行? → 取复杂度最大的那段
递归分治? → 套主定理
图算法? → 通常 O(n + m) 或 O(m log n)
常量次操作?→ O(1)
参考来源:OI Wiki - 复杂度简介(CC BY-SA 4.0)