分治算法( divide and conquer )的核心思想其实就是四个字,分而治之 ,也就是将原问题划分成 n 个规模较小,并且结构与原问题相似的子问题,递归地解决这些
子问题,然后再合并其结果,就得到原问题的解。
这个定义看起来有点类似递归的定义。关于分治和递归的区别,我们在排序(下)的时候讲过,分治算法是一种处理问题的思想,递归是一种编程技巧。实际
上,分治算法一般都比较适合用递归来实现。分治算法的递归实现中,每一层递归都会涉及这样三个操作:
分解:将原问题分解成一系列子问题;
解决:递归地求解各个子问题,若子问题足够小,则直接求解;
合并:将子问题的结果合并成原问题。
分治算法能解决的问题,
一般需要满足下面这几个条件:
原问题与分解成的小问题具有相同的模式;
原问题分解成的子问题可以独立求解,子问题之间没有相关性,这一点是分治算法跟动态规划的明显区别,等我们讲到动态规划的时候,会详细对比这两种
算法;
具有分解终止条件,也就是说,当问题足够小时,可以直接求解;
可以将子问题合并成原问题,而这个合并操作的复杂度不能太高,否则就起不到减小算法总体复杂度的效果了。
分治算法可以求解的一些经典问题
1二分搜索 2大整数乘法 3 棋盘覆盖 4 合并排序 5 快速排序 6线性时间选择 7 最接近点对问题 8 循环赛日程表 9 汉诺塔
分治(Divide-and-Conquer§)算法设计模式如下
分治算法的基本步骤
-
1 分治法在每一层递归上都有三个步骤:
-
2 分解:将原问题分解为若干个规模较小,相互独立,与原问题形式相同的子问题
-
3 解决:若子问题规模较小而容易被解决则直接解,否则递归地解各个子问题
-
4 合并:将各个子问题的解合并为原问题的解。
使用分治算法解决汉诺塔问题
汉诺塔问题
汉诺塔:汉诺塔(又称河内塔)问题是源于印度一个古老传说的益智玩具。大梵天创造世界的时候做了三根金
刚石柱子,在一根柱子上从下往上按照大小顺序摞着 64 片黄金圆盘。大梵天命令婆罗门把圆盘从下面开始按大小
顺序重新摆放在另一根柱子上。并且规定,在小圆盘上不能放大圆盘,在三根柱子之间一次只能移动一个圆盘。
分析:
A 如果是有一个盘, A->C
B 如果我们有 n >= 2 情况,我们总是可以看做是两个盘 1.最下边的盘 2. 上面的盘
-
1 先把 最上面的盘 A->B
-
2 把最下边的盘 A->C
-
3 把B塔的所有盘 从 B->C
可以看出 汉诺塔问题可以拆分成以上AB两个大步,分解成的子问题可以独立求解,子问题之间没有相关性
如果有多个盘则分成AB 步骤,对AB步骤进行求解
设n个盘子的移动次数为T(n)
T(n)=2T(n-1)+1
T(1)=1
所以Hanoi塔算法的时间复杂度为O(2^n)
public class DivideAndConquer {
public static void main(String[] args) {
hanoiProblem(3, 'A', 'B', 'C');
}
/*
* @param: [discNum, discA, discB, discC]
* @return: void
* @author: Jerssy
* @dateTime: 2021/5/3 16:47
* @description:
*/
public static void hanoiProblem(int discNum,char discA,char discB,char discC){
if (discNum == 1) {
System.out.println("the 1 disc move from "+discA+"-->"+discC);
}
else {
hanoiProblem(discNum-1, discA, discC, discB);
System.out.println("the "+discNum+" disc move from "+discA+"-->"+discC);
hanoiProblem(discNum-1, discB, discA, discC);
}
}
}