分治法——数据结构与算法学习

分治法

分治法思想

  1. 分解:将原问题分解为若干个规模较小,相互独立,与原问题形式相同的子问题
  2. 解决:若子问题规模较小而容易被解决则直接解,否则递归地解各个子问题
  3. 合并:将各个子问题的解合并为原问题的解。

分治法的实践

汉诺塔问题

思路

就是把复杂的问题简单化,拆分为一个个小问题:如果我们有 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

——说明是满足双递归的条件的。

思考

分治是一种思想,其实就是分解成最基础的一个问题,然后使用递归不断分解,把大问题分解成小问题,排序体现了这一点。

(不需要过于纠结递归的顺序,只需要记住逻辑:就是层层来分解就够了)

posted @   深海之燃  阅读(61)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 25岁的心里话
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示