第 4 章 分治法 (Divide and Conquer) 学习笔记
分治法是一种重要的算法设计范式,其核心思想是将一个难以直接解决的大问题,分割成一些规模较小的相同问题,便以此类推,直至问题规模小到可以轻易解决。
4.1 分治法的基本步骤与递归式
1. 如何解决递归问题
解决一个递归问题通常包含以下两个主要情况:
- Base Case (基本情况):问题规模足够小,可以直接求解。
- Recursive Case (递归情况):
- Divide (分解):将原问题分解为若干个规模较小的子问题。
- Conquer (解决):递归地求解各子问题。若子问题足够小,则直接求解。
- Combine (合并):将子问题的解合并为原问题的解。
2. 算法的递归描述式
一个分治算法的运行时间通常可以用递归方程表示:
T(n)={Θ(1)aT(n/b)+D(n)+C(n)n≤n0n>n0
其中:
- n0 是分水岭,区分了基本情况和递归情况。
- a 是分解出的子问题个数。
- n/b 是每个子问题的规模(通常 b=2)。
- D(n) 是分解问题的代价。
- C(n) 是合并解的代价。
注意:在理论分析时,虽然 n/b 往往需要取整(⌊n/b⌋ 或 ⌈n/b⌉),但为了简便,通常会省略取整符号。
4.2 求解递归式的三种方法
方法一:代入法 (Substitution Method)
标准操作流程 (SOP):
- 猜测:使用常数符号猜测解的形式(如 O(g(n)))。
- 证明:使用数学归纳法求出解中的常数,并证明解是正确的。
技巧:
- 缩小范围:可以通过证明 O 界(上界)和 Ω 界(下界)来夹逼出 Θ 界。
- 相似推导:相似的递归式通常有相似的解。例如 T(n)=2T(n/2+17)+Θ(n) 的解通常也是 O(nlgn)。
- 减去低阶项:为了使归纳法成立,有时需要从猜测中减去一个低阶项。
- 例题:T(n)=2T(n/2)+Θ(1)。若猜测 T(n)≤cn,推导时可能无法消掉常数项。此时改猜 T(n)≤cn−d(d 为常数),则更容易配平。
代入法实例推导
题目: 求解 T(n)=2T(⌊n/2⌋)+Θ(n)
猜测: T(n)=O(nlgn),即存在 c,n0>0,使得 ∀n>n0,T(n)≤cnlgn。
证明: 假设对于 n/2 成立,即 T(⌊n/2⌋)≤c⌊n/2⌋lg(⌊n/2⌋)。 代入递归式:
T(n)≤2(c⌊n/2⌋lg⌊n/2⌋)+Θ(n)≤2(c⋅2nlg2n)+Θ(n)=cn(lgn−lg2)+Θ(n)=cnlgn−cn+Θ(n)
只要 c 足够大,使得 cn≥Θ(n),则有 T(n)≤cnlgn。 结论: 猜测成立。
方法二:递归树法 (Recursion-tree Method)
递归树法将递归过程视觉化,每个节点代表该层子问题的代价。
实例: T(n)=3T(n/4)+Θ(n2)
- 根节点代价为 cn2。
- 第二层有 3 个子节点,每个代价为 c(n/4)2。
- 总代价即为所有层代价之和。
- 经计算,该递归项呈几何级数递减,总代价受根节点支配,T(n)=O(n2)。

方法三:主方法 (Master Method)
主方法是解决形如 T(n)=aT(n/b)+f(n) 递归式的“菜谱”。
分水岭函数: nlogba 比较 f(n) 与 nlogba 的增长速度:
- Case 1:f(n)=O(nlogba−ϵ)。 nlogba 增长更快,则 T(n)=Θ(nlogba)。
- Case 2:f(n)=Θ(nlogbalgkn)(通常 k=0)。 两者增长速度相近,则 T(n)=Θ(nlogbalgk+1n)。
- Case 3:f(n)=Ω(nlogba+ϵ) 且满足正则条件 af(n/b)≤cf(n)。 f(n) 增长更快,则 T(n)=Θ(f(n))。
4.3 经典案例分析
1. 归并排序 (Merge Sort)
递归式:T(n)=2T(n/2)+Θ(n) 通过主方法 Case 2 可知:T(n)=Θ(nlgn)。
2. 矩阵乘法与 Strassen 算法
普通分治法矩阵乘法
将 n×n 矩阵拆分为 4 个 (n/2)×(n/2) 的子矩阵。 计算 C=A⋅B 需要 8 次子矩阵乘法和 4 次矩阵加法。
- 递归式:T(n)=8T(n/2)+Θ(n2)
- 分析:根据主方法,nlog28=n3,由于 n3 远大于 f(n)=n2,属于 Case 1。
- 结果:T(n)=Θ(n3)。
- 思考:为什么普通分治没优化?因为“根系太多,树太茂盛”,递归分支(8个)过多导致计算量巨大。
Strassen 算法
核心贡献:将递归分支从 8 个减少到了 7 个。 实现方式:通过增加复杂的加法和减法,减少了 1 次关键的乘法。
- 递归式:T(n)=7T(n/2)+n2
- 分析:nlog27≈n2.81。
- 结果:T(n)=Θ(nlog27)=O(n2.81)。 虽然 Strassen 算法在常数项上较大,但在处理超大规模矩阵时,其渐进性能优于 Θ(n3)。

例题
1. 使用主方法 (Master Method) 求解递归式(CLRS 4.5-1)
主方法适用于形式为 T(n)=aT(n/b)+f(n) 的递归式。在本题中,a=2,b=4,临界函数为:
nlogba=nlog42=n1/2=n
a. T(n)=2T(n/4)+1
- 分析:f(n)=1。
- 比较:f(n)=O(n1/2−ϵ),其中 0<ϵ≤1/2。
- 结论:满足主方法情况 1。
- 解:T(n)=Θ(n)。
b. T(n)=2T(n/4)+n
- 分析:f(n)=n。
- 比较:f(n)=Θ(n1/2)。
- 结论:满足主方法情况 2。
- 解:T(n)=Θ(nlgn)。
c. T(n)=2T(n/4)+nlg2n
- 分析:f(n)=nlg2n。
- 比较:属于情况 2 的扩展(f(n)=Θ(nlogbalgkn),其中 k=2)。
- 结论:满足广义情况 2。
- 解:T(n)=Θ(nlg3n)。
d. T(n)=2T(n/4)+n
- 分析:f(n)=n。
- 比较:f(n)=Ω(n1/2+ϵ),其中 0<ϵ≤1/2。
- 正则性检查:af(n/b)=2(n/4)=n/2≤cn,当 1/2≤c<1 时成立。
- 结论:满足主方法情况 3。
- 解:T(n)=Θ(n)。
e. T(n)=2T(n/4)+n2
- 分析:f(n)=n2。
- 比较:f(n)=Ω(n1/2+ϵ),其中 0<ϵ≤3/2。
- 正则性检查:af(n/b)=2(n/4)2=n2/8≤cn2,当 1/8≤c<1 时成立。
- 结论:满足主方法情况 3。
- 解:T(n)=Θ(n2)。
💡 笔记提示:
- 情况 1:f(n) 增长慢于临界函数,解由临界函数决定。
- 情况 2:f(n) 与临界函数同阶,解需加一个 lgn 因子。
- 情况 3:f(n) 增长快于临界函数,解由 f(n) 决定(需满足正则性条件)。
要设计一个渐近性能快于 Strassen 算法(其复杂度约为 O(nlg7)≈O(n2.81))的算法,我们需要利用主方法来约束 a 的取值。
以下是整理后的详细推导过程:
1.2 渐近快于 Strassen 的分治算法设计(CLRS 4.5-2)
1. 建立递归模型
根据题目描述,我们将 n×n 矩阵分解为 n/4×n/4 的子矩阵。已知分解和合并的代价为 Θ(n2),设子问题个数为 a,则递归式为:
T(n)=aT(n/4)+Θ(n2)
2. 确定目标复杂度
Strassen 算法的运行时间为 T(n)=Θ(nlg7)。
为了使新算法渐近快于 Strassen 算法,我们需要满足:
T(n)=O(nlg7−δ)(其中 δ>0)
3. 应用主方法 (Master Method)
对于递归式 T(n)=aT(n/4)+Θ(n2):
- 临界项:nlog4a
- 驱动项:f(n)=Θ(n2)
由于我们需要算法尽可能快,其复杂度由递归树的叶子节点(即临界项)决定,这对应主方法的情况 1:
若 f(n)=O(nlog4a−ϵ),则 T(n)=Θ(nlog4a)。
4. 求解 a 的最大值
要使 T(n)=Θ(nlog4a) 优于 O(nlg7),必须满足:
log4a<lg7
利用换底公式 log4a=ln4lna 和 lg7=ln2ln7:
2ln2lna<ln2ln7
21lna<ln7
lna<2ln7=ln(72)
a<49
5. 结论
为了保证算法在渐近意义上严格快于 Strassen 算法,且 a 必须为整数:
验证:
- 若 a=48,则 T(n)=Θ(nlog448)≈Θ(n2.792)。
- Strassen 算法 T(n)=Θ(nlog27)≈Θ(n2.807)。
- 2.792<2.807,结论成立。
这份解答涵盖了使用代入法(Substitution Method)证明递归式复杂度的核心技巧。为了使逻辑更加清晰,我为你优化了排版,并对证明中的关键常数约束和归纳步骤进行了标注。
2. CLRS 4.3-1 使用代入法证明递归式
a. T(n)=T(n−1)+n⟹T(n)=O(n2)
-
猜测:T(n)≤cn2
-
证明:
T(n)≤c(n−1)2+n=c(n2−2n+1)+n=cn2−(2c−1)n+c
-
约束:要使 T(n)≤cn2,需 −(2c−1)n+c≤0,即 n(2c−1)≥c。
当 c≥1 且 n≥1 时成立(或 c>1/2 时对足够大的 n 成立)。
b. T(n)=T(n/2)+Θ(1)⟹T(n)=O(lgn)
-
猜测:T(n)≤clgn−b(注:减去常数 b 有助于处理 Θ(1))
-
证明:
T(n)≤clg(n/2)−b+d=c(lgn−1)−b+d=clgn−b−(c−d)
-
约束:只要 c≥d(其中 d 是 Θ(1) 的隐含常数),结论成立。
c. T(n)=2T(n/2)+n⟹T(n)=Θ(nlgn)
紧确界需要同时证明上界和下界。
-
上界 O(nlgn):
猜测 T(n)≤cnlgn。
T(n)≤2c(n/2)lg(n/2)+n=cn(lgn−1)+n=cnlgn−(c−1)n≤cnlgn(当 c≥1 时)。
-
下界 Ω(nlgn):
猜测 T(n)≥cnlgn。
T(n)≥2c(n/2)lg(n/2)+n=cnlgn−(c−1)n≥cnlgn(当 c≤1 时)。
d. T(n)=2T(n/2+17)+n⟹T(n)=O(nlgn)
-
技巧:由于存在 +17 项,直接使用 cnlgn 无法消去常数,需使用更强的归纳假设:T(n)≤c(n−a)lg(n−a)。
-
证明简述:
T(n)≤2c(n/2+17−a)lg(n/2+17−a)+n
令 a=34,则式子变为 c(n+34−2a)lg(n/2)+n=c(n−34)(lgn−1)+n。
展开后通过选择足够大的 c,可以证明其 ≤c(n−34)lg(n−34)。
e. T(n)=2T(n/3)+Θ(n)⟹T(n)=Θ(n)
-
上界 O(n):
猜测 T(n)≤cn。
T(n)≤2c(n/3)+dn=(2/3c+d)n。
只要 2/3c+d≤c,即 c≥3d,结论成立。
-
下界 Ω(n):
同理,取 c≤3d 即可证明 T(n)≥cn。
f. T(n)=4T(n/2)+Θ(n)⟹T(n)=Θ(n2)
-
上界 O(n2):
猜测 T(n)≤c1n2−c2n(减去低阶项以抵消 Θ(n))。
T(n)≤4(c1(n/2)2−c2(n/2))+dn=c1n2−2c2n+dn=c1n2−c2n−(c2−d)n。
当 c2≥d 时,上界成立。
-
下界 Ω(n2):
猜测 T(n)≥c1n2。
T(n)≥4c1(n/2)2+dn=c1n2+dn≥c1n2(对正数 d 显然成立)。
💡 专家提示:
在使用代入法时,如果发现数学推导无法消去多余的项,通常有两种策略:
- 减去低阶项:如 f 题中使用 cn2−dn。
- 变量代换:如 d 题中处理偏移量 +17 的技巧。
这份解答展示了如何通过递归树直观地观察工作量分布,并利用代入法严谨验证猜测。我为你优化了排版,补充了递归树的结构描述,并规范了数学符号和约束条件的表达。
4.4-1 递归树分析与代入法证明
a. T(n)=T(n/2)+n3
-
递归树描述:
- 根节点代价为 n3,下一层为 (n/2)3,再下一层为 (n/4)3。
- 这是一棵单支树(每个节点只有一个子节点)。
- 总代价为等比数列:n3(1+1/8+1/64+…),首项占主导。
-
猜测:T(n)=O(n3)。
-
代入法证明:
设 T(n)≤cn3:
T(n)≤c(n/2)3+n3=8cn3+n3=(8c+1)n3
要使 (8c+1)n3≤cn3,需 8c+1≤c,即 c≥8/7。
-
结论:T(n)=O(n3)。

b. T(n)=4T(n/3)+n
-
递归树描述:
- 第 i 层有 4i 个节点,每个节点的代价为 n/3i。
- 第 i 层的总代价为 (4/3)in。
- 树的高度为 log3n,叶子节点数量为 4log3n=nlog34≈n1.262。
- 代价随深度增加而增加(等比数列公比 >1),总代价由叶子层主导。
-
猜测:T(n)=O(nlog34)。
-
代入法证明:
设 T(n)≤cnlog34−dn(减去低阶项以抵消 f(n)=n):
T(n)≤4(c(n/3)log34−d(n/3))+n=4⋅4cnlog34−34dn+n=cnlog34−(34d−1)n
要使结果 ≤cnlog34−dn,需 34d−1≥d,即 d≥3。
-
结论:T(n)=O(nlog34)。

c. T(n)=4T(n/2)+n
-
递归树描述:
- 第 i 层总代价为 4i⋅(n/2i)=2in。
- 叶子节点数量为 4log2n=n2。
- 总代价为等比数列 n+2n+4n+⋯+n2,由末项主导。
-
猜测:T(n)=O(n2)。
-
代入法证明:
设 T(n)≤cn2−dn:
T(n)≤4(c(n/2)2−d(n/2))+n=cn2−2dn+n=cn2−(2d−1)n
要使结果 ≤cn2−dn,需 2d−1≥d,即 d≥1。
-
结论:T(n)=O(n2)。
-

d. T(n)=3T(n−1)+1
-
递归树描述:
- 树退化为高度为 n 的高度分支树,每层代价为 3i。
- 总代价为 ∑i=0n−13i=23n−1。
-
猜测:T(n)=O(3n)。
-
代入法证明:
设 T(n)≤c3n−d(d 为常数):
T(n)≤3(c3n−1−d)+1=c3n−3d+1
要使结果 ≤c3n−d,需 3d−1≥d,即 d≥1/2。
-
结论:T(n)=O(3n)。
-

💡 归纳总结:
- 当递归树的每层代价逐层递减(如 a),复杂度由根节点决定。
- 当递归树的每层代价基本相等,复杂度通常带 lgn 因子。
- 当递归树的每层代价逐层递增(如 b, c, d),复杂度由叶子节点决定。