分治算法

分治的关键不是“递归”两个字,而是把原问题拆成若干个同类子问题,分别解决后还能正确合并回来。

核心思路

分治通常盯三件事:

  1. 怎么拆。
  2. 什么时候停。
  3. 怎么合。

如果只能拆、不能高效合并,或者子问题之间大量重叠,那就不一定适合纯分治,可能更像 动态规划。

高频专题

排序类

归并排序(Merge Sort)

  • 分:从中间拆开
  • 治:分别排好左右两半
  • 合:线性归并成一个有序数组

快速排序(Quick Sort)

  • 分:围绕 pivot 划分
  • 治:递归处理左右区间
  • 合:原地完成,不需要显式归并

数组与查找类

最大子数组和(Maximum Subarray)

  • 典型的“左边最优、右边最优、跨中点最优”三合一

寻找两个正序数组的中位数

  • 本质上是规模缩减式分治

数组中的逆序对

  • 利用归并时的相对顺序顺手统计答案

数学类

快速幂(Fast Power)

  • 指数减半,时间从 O(n) 降到 O(log n)

大整数乘法(Karatsuba算法)

  • 用更巧妙的拆分减少乘法次数

矩阵乘法(Strassen算法)

  • 了解思想即可,工程中不一定常用

几何与经典题

最近点对问题

  • 经典分治几何题

汉诺塔(Tower of Hanoi)

  • 它更像“用最纯的递归去理解分治结构”

怎么判断能不能分治

满足下面三条时,通常值得往分治想:

  1. 原问题和子问题长得足够像。
  2. 子问题之间基本独立。
  3. 合并阶段不会比求子问题本身更贵。

分治 vs 动态规划

特性分治算法动态规划
子问题相互独立有重叠
重复计算无有(需要记忆化)
求解方向自顶向下自底向上或记忆化
典型例子归并排序背包问题

Go 模板

func solve(problem Problem) Answer {
	if smallEnough(problem) {
		return directSolve(problem)
	}
 
	left, right := split(problem)
	leftAns := solve(left)
	rightAns := solve(right)
	return merge(leftAns, rightAns)
}

易错点

分治题最容易写成“看起来像递归,实际上没有高效利用分治结构”。

  • 递归出口不清晰会直接爆栈。
  • 合并逻辑通常才是整题最难的部分。
  • 有些题分治能做,但不是最优,比如最大子数组和常用解其实是 DP。
  • 快排类题要特别注意最坏情况和 pivot 选择。

相关主题


返回:算法学习导航