分治法(Divide & Conquer)在算法设计里几乎无处不在,凡是能把“整体问题”拆成“结构相同、规模更小”的子问题,再把子问题的解合并成整体解的场景,它都适用。下面按“经典”到“日常”再到“前沿”三个层次给出常见或具有代表性的应用场景(每个都附一句话说明其分治要点,方便快速回忆)。
一、教科书级经典
- 归并排序(Merge Sort)
把数组对半分→分别递归排序→线性合并两段有序数组。 - 快速排序(Quick Sort)
选枢轴把数组分成“小”与“大”两块→分别递归→自然合并(原地)。 - 二分查找(Binary Search)
有序区间中点比较→舍弃一半,递归/迭代另一半。 - 最大子段和(Kadane 的“分治版”)
把数组对半分→最大子段要么全在左、全在右,或跨越中点→三种情况取最大。 - 最近点对(Closest Pair of Points)
按 x 坐标划分平面→左右各求最近对→中间带状区再检查“可能更近”的点对。
二、日常工程常用
6. Strassen 矩阵乘法
把 n×n 矩阵拆成 4 个 n/2×n/2 子矩阵→7 次乘法 + 18 次加法→O(n^2.81)。
7. Karatsuba/Toom-Cook 大整数乘法
把两个 n 位整数各拆成高、低 n/2 位→3 次 n/2 位乘法→O(n^log_2 3)。
8. 快速傅里叶变换(FFT)
多项式按偶次/奇次项分治→递归求两半点值→蝴蝶操作合并。
9. 快速幂(Exponentiation by Squaring)
指数对半分→a^n = (a(n/2))2(或再乘一次 a)→O(log n)。
10. 线段树 / 树状数组
区间查询/更新→把区间折半→左右子树递归→合并结果。
11. 平衡树(如 AVL、红黑树)的批量构建
先排序→中序分治递归建子树→O(n) 构造。
12. MapReduce 中的分布式排序 / 词频统计
数据按 key 哈希分片→各节点本地排序/计数→全局归并。
三、前沿或竞赛级
13. 整数乘法 O(n log n)(Schönhage–Strassen, Fürer, Harvey–van der Hoeven)
在 FFT 基础上再套多层分治。
14. 凸包(Convex Hull)
按 x 排序后分治→左右各求凸包→合并两条“上桥/下桥”。
15. 逆序数统计(归并排序版)
归并过程顺带统计跨左右逆序对→O(n log n)。
16. 平面/三维点云的分治 Delaunay 三角化
按 x 坐标分链→递归三角化→“基线合并”保证 Delaunay 性质。
17. 大规模稀疏矩阵的并行分块乘法(SpGEMM)
把矩阵按行列块递归→子块乘法→结果累加。
18. 分布式图算法(如 Pregel/GraphX 的 BSP 模型)
超步内局部计算→全局消息交换→下一轮分治式迭代。
一句话总结:
“任何可以切成‘同质小块’、小块又能拼回整体的问题,几乎都能用分治法提速或并行化。”
汉诺塔问题是分治法的经典应用,其核心思想是将复杂问题拆解为规模更小的同类子问题,通过解决子问题逐步推导最终解。以下是用分治法解决汉诺塔问题的详细步骤:
问题背景
- 初始状态:有3根柱子(A、B、C),n个大小不同的圆盘按“小在上、大在下”的顺序堆叠在A柱上。
- 目标:将所有圆盘从A柱移动到C柱,每次只能移动1个圆盘,且任何时候都不能让大圆盘压在小圆盘上。
分治法解决步骤
分治法的核心是“拆解-求解-合并”,具体到汉诺塔问题:
1. 基础情况(最小子问题)
当只有1个圆盘(n=1)时,直接将圆盘从A柱移动到C柱,即:
A → C
这是无需拆解的最小问题,也是递归的终止条件。
2. 拆解问题(n≥2时)
对于n个圆盘,分治法将其拆解为3个步骤,利用B柱作为“中间辅助柱”:
- 子问题1:将A柱上的前n-1个圆盘从A柱移动到B柱(此时C柱作为辅助柱)。
- 子问题2:将A柱上剩下的第n个(最大的)圆盘从A柱移动到C柱。
- 子问题3:将B柱上的n-1个圆盘从B柱移动到C柱(此时A柱作为辅助柱)。
3. 递归求解子问题
通过递归调用上述步骤,逐步缩小问题规模,直到达到基础情况(n=1)。
每一步的子问题本质与原问题完全一致,只是圆盘数量减少且柱子角色互换(辅助柱、源柱、目标柱的定义随步骤变化)。
示例:n=3时的具体移动过程
以3个圆盘为例,演示分治法的执行流程:
-
子问题1:将A柱的前2个圆盘移动到B柱(C辅助)
- ① 将A的第1个圆盘移到C(A→C)
- ② 将A的第2个圆盘移到B(A→B)
- ③ 将C的第1个圆盘移到B(C→B)
(此时B柱有2个圆盘,A柱剩最大圆盘,C柱空)
-
子问题2:将A柱的第3个(最大)圆盘移到C(A→C)
(此时A柱空,C柱有最大圆盘,B柱有2个圆盘) -
子问题3:将B柱的2个圆盘移动到C柱(A辅助)
- ④ 将B的第1个圆盘移到A(B→A)
- ⑤ 将B的第2个圆盘移到C(B→C)
- ⑥ 将A的第1个圆盘移到C(A→C)
递归函数的抽象表示
用函数hanoi(n, 源柱, 辅助柱, 目标柱)
表示“将n个圆盘从源柱移动到目标柱,以辅助柱为中间媒介”,则步骤可抽象为:
def hanoi(n, A, B, C):
if n == 1:
print(f"{A} → {C}") # 基础情况
else:
hanoi(n-1, A, C, B) # 子问题1:n-1个从A→B(C辅助)
print(f"{A} → {C}") # 子问题2:最大圆盘A→C
hanoi(n-1, B, A, C) # 子问题3:n-1个从B→C(A辅助)
总结
分治法通过将n个圆盘的问题拆解为“移动n-1个圆盘→移动最大圆盘→再移动n-1个圆盘”,利用递归逐步简化问题,最终通过解决最小子问题(n=1)完成所有移动。其移动次数为2ⁿ - 1
(n为圆盘数量),体现了分治法“以空间换时间”的特点。
汉诺塔问题的时间复杂度可以通过递归分析得出,结果为 O(2ⁿ),其中 n 是圆盘的数量。
具体分析过程:
-
递归关系建立
设移动 n 个圆盘所需的最少步数为 T(n)。根据分治法的拆解逻辑:- 移动 n 个圆盘时,需先将 n-1 个圆盘从源柱移到辅助柱(步数为 T(n-1));
- 再将第 n 个(最大的)圆盘从源柱移到目标柱(1 步);
- 最后将 n-1 个圆盘从辅助柱移到目标柱(步数为 T(n-1))。
因此,递归关系式为:
T(n) = 2×T(n-1) + 1
-
求解递归关系式
- 基础情况:当 n=1 时,T(1) = 1(直接移动 1 个圆盘)。
- 展开递归:
T(n) = 2×T(n-1) + 1 = 2×(2×T(n-2) + 1) + 1 = 2²×T(n-2) + 2 + 1 = 2³×T(n-3) + 2² + 2 + 1 ... = 2ⁿ⁻¹×T(1) + 2ⁿ⁻² + ... + 2² + 2 + 1
- 代入 T(1)=1 并化简等比数列:
T(n) = 2ⁿ - 1
-
时间复杂度结论
由于 T(n) = 2ⁿ - 1,其增长趋势与 2ⁿ 一致,因此汉诺塔问题的时间复杂度为 O(2ⁿ),属于指数级复杂度。这意味着当 n 增大时(如 n=30),所需步数会急剧增加(2³⁰≈10亿),体现了指数级算法的高效性局限。