分治算法详细讲解

核心思想:分(拆成子问题)→治(递归求解子问题)→合(合并子结果),适合大规模问题拆解,高频场景:排序、查找、统计、矩阵、最近点对等。

每道题包含题意、分治思路、完整可运行代码。

例题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:归并排序(标准分治排序)

题意

递归实现数组升序排序。

分治思路

  1. 分:数组拆为左右两半;

  2. 治:递归排序左右子数组;

  3. 合:有序合并两个子数组。

    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:最近点对(平面点集分治经典)

题意

平面一堆坐标点,求距离最近两个点的欧几里得距离。

分治思路

  1. 分:按x坐标中点拆分左右点集;

  2. 治:递归求左右最小距离d1、d2,d=min(d1,d2);

  3. 合:只检查中线左右宽度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. 二分查找类:例题1、例题7

  2. 排序类标准分治:例题2归并排序、例题3快速排序

  3. 数组统计分治:例题4最大值、例题5逆序对

  4. 数学计算分治:例题6快速幂

  5. 树结构分治:例题8二叉树深度

  6. 矩阵分治:例题9矩阵乘法

  7. 计算几何经典分治:例题10

相关推荐
code bean1 小时前
平衡相关性与多样性:推荐系统中的永恒博弈与 MMR 算法详解
算法
摇滚侠2 小时前
Mybatis 入门到项目实战 搭建 MyBatis 框架 01-14
java·tomcat·mybatis
青梅橘子皮2 小时前
Linux---进程控制(2)(进程程序替换)
linux·c++·算法
老马聊技术2 小时前
AI对话功能之SpringBoot整合Vue3
vue.js·人工智能·spring boot·后端
Shan12052 小时前
经典问题——验证栈序列
数据结构·算法
2501_906565122 小时前
勾股定理证明
算法
武子康2 小时前
调查研究-174 什么是“红丸主义“:它为什么吸引人,又为什么容易把人带偏?
后端
神奇小汤圆2 小时前
白嫖DeepSeek V4 Pro!免费无限用,还能接入Claude-Code
后端
码不停蹄的玄黓2 小时前
SpringBoot 全局异常处理器实现
java·spring boot·后端