渐近分析关心 n 足够大时的增长速度,忽略常数倍和低阶项。它不是“把公式写得模糊一点”,而是用严格量词描述函数之间的长期关系。
1. 五个渐近记号
设 f(n),g(n) 在充分大的 n 上非负。
1.1 渐近上界 O
f(n)=O(g(n))
表示存在常数 c>0,n0,使所有 n≥n0 都有
0≤f(n)≤cg(n).
它只表示“不比 g 增长得更快”。例如 3n+7=O(n2) 也正确,只是不紧。
1.2 渐近下界 Ω
f(n)=Ω(g(n))
表示存在 c>0,n0,使 n≥n0 时
0≤cg(n)≤f(n).
1.3 紧确界 Θ
f(n)=Θ(g(n))
表示同时有 f(n)=O(g(n)) 和 f(n)=Ω(g(n))。也就是 g 在常数倍意义下准确描述了 f 的增长阶。
1.4 严格上界 o 与严格下界 ω
f(n)=o(g(n))⟺n→∞limg(n)f(n)=0,
f(n)=ω(g(n))⟺n→∞limg(n)f(n)=∞.
n=o(n2),但 n=o(n)。小写记号排除了“同阶”的情况。
2. 用极限快速比较
考察
L=n→∞limg(n)f(n).
- 0<L<∞:f=Θ(g);
- L=0:f=o(g);
- L=∞:f=ω(g)。
极限不存在时,不能直接下结论。例如 f(n)=n(2+sinn) 与 n 的比值不收敛,但始终夹在 n 和 3n 之间,所以仍有 f(n)=Θ(n)。
3. 常见增长顺序
对固定常数 k>0、a>1:
1≺loglogn≺logn≺(logn)k≺nε≺n≺nk≺an≺n!≺nn.
这里 ε>0 可以很小。一个重要结论是:任意正次数多项式最终都比任意对数幂增长快,而任意固定底数指数最终都比任意多项式增长快。
对数底数只差常数:
logan=logbalogbn=Θ(logn).
4. 多项式、和与乘积
对多项式
p(n)=adnd+⋯+a1n+a0,
若 ad>0,则 p(n)=Θ(nd)。
常用规则:
O(f)+O(g)=O(max{f,g}),
O(f)⋅O(g)=O(fg).
顺序执行的两段程序取较大复杂度;嵌套执行通常相乘。但“循环嵌套”不一定机械相乘,内层迭代次数可能依赖外层变量,必须求和。
5. 常见求和
5.1 等差与幂和
i=1∑ni=Θ(n2),i=1∑nik=Θ(nk+1).
5.2 调和级数
i=1∑ni1=Θ(logn).
因此下面循环不是 O(n2):
for i = 1 .. n:
for j = 1; j <= n; j += i:
constant_work()
第 i 轮约执行 n/i 次,总计
ni=1∑ni1=Θ(nlogn).
5.3 几何级数
当 0<r<1 时,
1+r+r2+⋯=Θ(1).
当 r>1 时,有限几何级数由最后一项主导。这是分析递归树的核心工具。
6. 传递性与对称性
- O、Ω、Θ 都具有传递性;
- f=Θ(g) 具有对称性;
- f=O(g) 等价于 g=Ω(f);
- 若 f=O(g),不能推出 f=Θ(g)。
写“算法复杂度是 O(n2)”通常是在报上界。如果已经证明上下界一致,写 Θ(n2) 更准确。
7. 空间复杂度也用同一套记号
原地算法通常指额外空间 O(1),但要留意:
- 递归调用栈;
- 返回数组或复制切片;
- 哈希表和队列的最坏规模;
- 输入是否允许被修改。
例如递归二分查找时间 O(logn),递归栈空间也是 O(logn);改成迭代后可降为 O(1)。
8. 易错点
- O 不是“等于最坏情况”,它只是函数上界;最好、平均、最坏三种情况都能使用 O。
- 常数在渐近分析中可忽略,但在实际工程里仍可能决定哪个算法更快。
- O(2n) 与 O(n) 相同,O(2n) 与 O(n) 完全不同。
- 不能把 O(f)+O(g) 写成 O(f+g) 后就停止;应继续化简到主导项。
- 对多参数问题不要强行只写一个 n,例如图算法应保留 V,E。