递归算法的时间复杂度通常写成“子问题成本 + 当前层成本”。求解递推式的关键不是背答案,而是看清每层有多少子问题、每个多大、当前层做多少额外工作。
1. 直接展开
例如
T(n)=T(n−1)+n,T(1)=Θ(1).
连续展开:
T(n)=T(1)+2+3+⋯+n=Θ(n2).
再如
T(n)=T(n/2)+1,
每次规模减半,展开 log2n 次后到达 1,所以 T(n)=Θ(logn)。
直接展开适合单分支递归和容易求和的递推式。
2. 代入法
代入法本质是“先猜答案,再用归纳法证明”。例如猜
T(n)≤cnlogn.
对
T(n)=2T(n/2)+n
代入归纳假设:
T(n)≤2c2nlog2n+n=cnlogn−cn+n.
当 c≥1 时不超过 cnlogn,上界成立。再证明同阶下界即可得到 Θ(nlogn)。
若差一个低阶项导致归纳闭合不了,可加强猜想,例如从 cn 改成 cn−d。这不是作弊,而是在给归纳留出“余量”。
3. 递归树
考虑
T(n)=3T(n/4)+cn2.
第 i 层有 3i 个规模 n/4i 的子问题,因此该层非递归成本为
3ic(4in)2=cn2(163)i.
这是收敛几何级数,根层工作占主导,所以 T(n)=Θ(n2)。
递归树最直观的价值是判断:
- 每层成本在下降:根层主导;
- 每层成本相同:层数带来一个 logn;
- 每层成本在上升:叶子层主导。
4. 主定理
对
T(n)=aT(n/b)+f(n),
递归树有 logbn 层,叶子数约为
alogbn=nlogba.
因此把 f(n) 与 nlogba 比较。
4.1 子问题主导
若存在 ε>0,使
f(n)=O(nlogba−ε),
则叶子贡献更大:
T(n)=Θ(nlogba).
4.2 各层同阶
若
f(n)=Θ(nlogbalogkn),
则
T(n)=Θ(nlogbalogk+1n).
最常见的 k=0 情形就是多乘一个 logn。
4.3 根层主导
若存在 ε>0,使
f(n)=Ω(nlogba+ε),
并满足正则条件
af(n/b)≤cf(n),c<1,
则
T(n)=Θ(f(n)).
正则条件保证 f 不会剧烈波动,使根层确实占主导。
5. 典型例题
| 递推式 | 比较 | 结果 |
|---|
| 2T(n/2)+n | f(n)=nlog22 | Θ(nlogn) |
| 4T(n/2)+n | 叶子规模 n2 更大 | Θ(n2) |
| T(n/2)+n | 根层工作更大 | Θ(n) |
| 2T(n/2)+nlogn | 临界项多一个 logn | Θ(nlog2n) |
| 7T(n/2)+n2 | nlog27 更大 | Θ(nlog27) |
6. 主定理不能乱套
以下递推式不能直接套标准主定理:
- T(n)=T(n−1)+n:子问题不是 n/b;
- T(n)=T(n/2)+T(n/4)+n:子问题规模不同;
- T(n)=2nT(n/2)+n:a 不是常数;
- T(n)=2T(n/2)+nloglogn:不一定落入最基础三种模板。
这时可用展开、递归树、Akra-Bazzi 定理或上下界夹逼。
7. 非等分子问题的递归树
例如
T(n)=T(n/2)+T(n/4)+T(n/8)+n.
下一层所有子问题规模之和是 7n/8,所以每层总代价按 7/8 递减:
n+87n+(87)2n+⋯=Θ(n).
无需先求出叶子数,也能看出 T(n)=Θ(n)。
8. 写答案的规范顺序
- 写清基础情形;
- 说明采用的方法;
- 展示每层代价或主定理参数;
- 求和;
- 用 Θ 给出紧确结果。
只写“由主定理得”而不列 a,b,f(n),考试时很容易因套错情形丢分。