核心思想:分(拆成子问题)→治(递归求解子问题)→合(合并子结果),适合大规模问题拆解,高频场景:排序、查找、统计、矩阵、最近点对等。
每道题包含题意、分治思路、完整可运行代码。
例题1:二分查找(最基础分治)
题意
升序数组,查找目标值target下标,不存在返回-1。
分治思路
不断折半拆分搜索区间,只在单侧子区间递归查找。
public class DivideConquer {
public int binarySearch(int\[\] nums, int target, int l, int r) {
if (l > r) return -1;
int mid = l + (r - l) / 2;
if (numsmid == target) return mid;
else if (numsmid > target)
return binarySearch(nums, target, l, mid - 1);
else
return binarySearch(nums, target, mid + 1, r);
}
}
例题2:归并排序(标准分治排序)
题意
递归实现数组升序排序。
分治思路
-
分:数组拆为左右两半;
-
治:递归排序左右子数组;
-
合:有序合并两个子数组。
public void mergeSort(int\[\] nums, int l, int r) {
if (l >= r) return;
int mid = l + (r - l) / 2;
// 分
mergeSort(nums, l, mid);
mergeSort(nums, mid + 1, r);
// 合并
merge(nums, l, mid, r);
}
private void merge(int\[\] nums, int l, int mid, int r) {
int\[\] temp = new intr - l + 1;
int i = l, j = mid + 1, k = 0;
while (i <= mid && j <= r) {
tempk++ = numsi < numsj ? numsi++ : numsj++;
}
while (i <= mid) tempk++ = numsi++;
while (j <= r) tempk++ = numsj++;
System.arraycopy(temp, 0, nums, l, temp.length);
}
例题3:快速排序(分治经典)
题意
递归快排数组。
分治思路
选基准值划分区间,小元素放左、大元素放右,再递归左右子区间。
public void quickSort(int\[\] nums, int l, int r) {
if (l >= r) return;
int pivot = partition(nums, l, r);
quickSort(nums, l, pivot - 1);
quickSort(nums, pivot + 1, r);
}
private int partition(int\[\] nums, int l, int r) {
int val = numsl;
int i = l, j = r;
while (i < j) {
while (i < j && numsj >= val) j--;
numsi = numsj;
while (i < j && numsi <= val) i++;
numsj = numsi;
}
numsi = val;
return i;
}
例题4:求数组最大值(分治)
题意
不用循环遍历,递归拆分求整个数组最大值。
分治思路
数组对半拆分,分别求左右最大值,合并取更大值。
public int getMax(int\[\] nums, int l, int r) {
if (l == r) return numsl;
int mid = l + (r - l) / 2;
int leftMax = getMax(nums, l, mid);
int rightMax = getMax(nums, mid + 1, r);
return Math.max(leftMax, rightMax);
}
例题5:数组逆序对数量(归并分治经典)
题意
i<j 且 numsi>numsj 称为一对逆序对,统计总数。
分治思路
拆分左右,分别统计内部逆序对,合并时统计跨区间逆序对。
private int count = 0;
public int reversePairs(int\[\] nums) {
count = 0;
mergeCount(nums, 0, nums.length - 1);
return count;
}
private void mergeCount(int\[\] nums, int l, int r) {
if (l >= r) return;
int mid = l + (r - l) / 2;
mergeCount(nums, l, mid);
mergeCount(nums, mid + 1, r);
calcPairs(nums, l, mid, r);
}
private void calcPairs(int\[\] nums, int l, int mid, int r) {
int\[\] tmp = new intr - l + 1;
int i = l, j = mid + 1, idx = 0;
while (i <= mid && j <= r) {
if (numsi <= numsj) tmpidx++ = numsi++;
else {
tmpidx++ = numsj++;
count += mid - i + 1;
}
}
while (i <= mid) tmpidx++ = numsi++;
while (j <= r) tmpidx++ = numsj++;
System.arraycopy(tmp, 0, nums, l, tmp.length);
}
例题6:幂运算快速幂(分治)
题意
计算 x^n,不用循环连乘,二分拆分指数降低时间复杂度。
分治思路
n偶数:xn=(x{n/2})2;n奇数:xn=x\cdot(x{n/2})2。
public double quickPow(double x, long n) {
if (n == 0) return 1.0;
if (n < 0) return 1.0 / quickPow(x, -n);
double half = quickPow(x, n / 2);
if (n % 2 == 0) return half * half;
else return half * half * x;
}
例题7:寻找旋转排序数组最小值(分治二分)
题意
升序数组旋转后,无重复元素,找最小值。
public int findMin(int\[\] nums) {
return findMinDc(nums, 0, nums.length - 1);
}
private int findMinDc(int\[\] nums, int l, int r) {
if (l == r) return numsl;
int mid = l + (r - l) / 2;
// 右侧无序,最小值在右半边
if (numsmid > numsr)
return findMinDc(nums, mid + 1, r);
else
return findMinDc(nums, l, mid);
}
例题8:二叉树深度(分治递归)
题意
求二叉树最大深度。
分治思路
当前树深度 = max(左子树深度, 右子树深度)+1
static class TreeNode {
int val;
TreeNode left, right;
TreeNode(int v) { val = v; }
}
public int maxDepth(TreeNode root) {
if (root == null) return 0;
int leftDep = maxDepth(root.left);
int rightDep = maxDepth(root.right);
return Math.max(leftDep, rightDep) + 1;
}
例题9:矩阵乘法(分治版,Strassen简化思想)
简化分治思路
把大矩阵拆成4个子矩阵递归相乘,再合并结果;下面给出分治框架。
// 仅演示分治拆分逻辑,完整矩阵合并可自行补全
public void matrixMult(int\[\]\[\] A, int\[\]\[\] B, int\[\]\[\] C, int n) {
if (n == 1) {
C00 = A00 * B00;
return;
}
int half = n / 2;
// 拆分成A11,A12,A21,A22;B同理,递归计算子块乘积,再合并赋值给C
// 递归分治调用子矩阵相乘
}
例题10:最近点对(平面点集分治经典)
题意
平面一堆坐标点,求距离最近两个点的欧几里得距离。
分治思路
-
分:按x坐标中点拆分左右点集;
-
治:递归求左右最小距离d1、d2,d=min(d1,d2);
-
合:只检查中线左右宽度d带状区域内点,更新全局最小距离。
import java.util.*;
class Point {
double x, y;
Point(double x, double y) { this.x = x; this.y = y; }
}
// 两点距离
private double dist(Point a, Point b) {
double dx = a.x - b.x, dy = a.y - b.y;
return Math.sqrt(dxdx + dy dy);
}
// 暴力枚举少量点
private double bruteForce(List pts, int l, int r) {
double min = Double.MAX_VALUE;
for (int i=l; i<=r; i++)
for (int j=i+1; j<=r; j++)
min = Math.min(min, dist(pts.get(i), pts.get(j)));
return min;
}
// 分治主函数
private double closestPair(List pts, int l, int r) {
if (r - l <= 3) return bruteForce(pts, l, r);
int mid = (l + r) / 2;
double midX = pts.get(mid).x;
double dLeft = closestPair(pts, l, mid);
double dRight = closestPair(pts, mid+1, r);
double d = Math.min(dLeft, dRight);
// 收集中线附近d宽度内的点
List<Point> strip = new ArrayList<>();
for (int i=l; i<=r; i++) {
if (Math.abs(pts.get(i).x - midX) < d)
strip.add(pts.get(i));
}
// 按y排序后只和后续几个点比对
strip.sort(Comparator.comparingDouble(p -> p.y));
double stripMin = d;
for (int i=0; i<strip.size(); i++) {
for (int j=i+1; j<strip.size() && strip.get(j).y-strip.get(i).y<stripMin; j++) {
stripMin = Math.min(stripMin, dist(strip.get(i), strip.get(j)));
}
}
return Math.min(d, stripMin);
}
10道例题分类汇总
-
二分查找类:例题1、例题7
-
排序类标准分治:例题2归并排序、例题3快速排序
-
数组统计分治:例题4最大值、例题5逆序对
-
数学计算分治:例题6快速幂
-
树结构分治:例题8二叉树深度
-
矩阵分治:例题9矩阵乘法
-
计算几何经典分治:例题10