分治法——数据结构与算法学习
分治法
分治法思想
- 分解:将原问题分解为若干个规模较小,相互独立,与原问题形式相同的子问题
- 解决:若子问题规模较小而容易被解决则直接解,否则递归地解各个子问题
- 合并:将各个子问题的解合并为原问题的解。
分治法的实践
汉诺塔问题
思路:
就是把复杂的问题简单化,拆分为一个个小问题:如果我们有 n >= 2 情况,我们总是可以看做是两个盘 1.最下边的盘 2. 上面的盘,然后也可以用下面的方式操作:
package hanluota;
public class Hanoitower {
public static void main(String[] args) {
hanoiTower(3,'A','B','C');
}
//分治算法,这里的数量都是经过抽象处理过的,以B为中介,把A移动到C
public static void hanoiTower(int num,char a, char b,char c){
if (num == 1){//最后一个盘一定是a放到c上
System.out.println("第1个盘从"+ a + "->" + c );
}else{
hanoiTower(num - 1,a,c,b);//找到值以后会回退,这里以b作为中介
System.out.println("第"+ num + "个盘从" + a + "->" + c);//保证最下面的盘放到目标位置
hanoiTower(num - 1,b,a,c);//这里以a作为中介,把b移动到c上
}
}
}
这里值得注意的地方:
我们必须要正确理解两个连续递归是如何来进行执行的。
首先看一个简单的例子:
ublic class RecursionTest {
public static void main(String[] args) {
rec(11);
}
public static void rec(int n){
if(n > 0){
rec(n - 1);
rec(n - 10);
}
System.out.println("n = " + n);
}
}
输出结果:
n = 0
n = -9
n = 1
n = -8
n = 2
n = -7
n = 3
n = -6
n = 4
n = -5
n = 5
n = -4
n = 6
n = -3
n = 7
n = -2
n = 8
n = -1
n = 9
n = 0
n = 10
n = 0
n = -9
n = 1
n = 11
这里我们可以用栈结构来进行解释结果的输出:
首先自上而下执行第一个递归,在遇到递归结束条件后开始返回,在第一个递归的返回过程中执行第二个递归,形成交替执行的感觉。
下表左面是第一个递归形成的栈,右边是在回退时进行第二个递归形成的栈。
0 | 未执行 |
---|---|
1 | -9(因为执行顺序在前,所以先输出) |
2 | -8 |
3 | -7 |
4 | -6 |
5 | -5 |
6 | -4 |
7 | -3 |
8 | -2 |
9 | -1 |
10 | 0 |
为什么后面又输出4个数呢:
实际上是在输出11,之前,递归了rec(1)和rec(1) 和 rec(-9),因此形成了0,-9,1,第一个递归调用了一次,第二次递归调用了两次。
由此我们可以得到汉诺塔问题中双递归的执行逻辑:
对于n个盘子,均为先执行将A-B,然后再执行B-C。
归并排序
思路:

public static void mergeSort(int[] arr,int left,int right,int[] temp){
//正确理解双递归是理解归并排序的关键,必须要清楚多递归运行的顺序,才能真正入门分治算法。
if(left < right){
int mid = (left + right)/2;
mergeSort(arr,left,mid,temp);
mergeSort(arr,mid + 1,right,temp);
merge(arr,left,mid,right,temp);
}
}
public static void merge(int arr[],int left,int mid,int right,int[] temp){
int i = left;//左边序列的初始索引
int j = mid + 1;//右边序列的初始索引
int t = 0;//指向temp数组的索引
while(i <= mid && j <= right){
if(arr[i] <= arr[j]){
temp[t] = arr[i];
t += 1;
i += 1;
}else{
temp[t] = arr[j];
t += 1;
j += 1;
}
}
while(i <= mid){
temp[t] = arr[i];
t += 1;
i += 1;
}
while(j <= right){
temp[t] = arr[j];
t += 1;
j += 1;
}
//完成数组的拷贝(这一块没看懂,再想一下)
t = 0;
int tempLeft = left;
System.out.println("tempLeft ="+ tempLeft + " right="+ right);
while(tempLeft <= right){
arr[tempLeft] = temp[t];
t += 1;
tempLeft += 1;
}
}
思考:通过代码发现合并的索引就是0,1;2,3;0,3;4,5;6,7;4,7
——说明是满足双递归的条件的。
思考
分治是一种思想,其实就是分解成最基础的一个问题,然后使用递归不断分解,把大问题分解成小问题,排序体现了这一点。
(不需要过于纠结递归的顺序,只需要记住逻辑:就是层层来分解就够了)
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 25岁的心里话
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现