分治算法
分治算法的设计思想
- 分解
将原问题分解成若干个规模较小的、相互独立的、与原问题形式相同的子问题; - 解决
当子问题规模小到一个程度可以直接求解的时候直接解,否则往下递归求解; - 合并
将各个子问题的解合并为源问题的解
汉诺塔问题
-
如果是手动操作的思路分析(比如4个盘)
- 如果想把A柱上4个盘全部移到C柱,则首先需要将最下面的盘移到C柱, 就需要将上面3个圆盘先移到B柱;
- 如果想把A柱上面3个盘的最下面一个盘(倒数第二个)移到B柱,就需要先将上面2个盘移到
C柱; - 如果想把A柱上面2个盘的最下面1个盘(倒数第三个)移到C柱,就需要先将上面1个盘移到B柱
至此,就得到了第一步应该怎么走!
-
分治算法思路分析
源问题就是将A柱上的N个盘借助B柱移到C柱,则可以将其分解成:- 将上面的N-1的盘子放到B柱
- 将最下面的一个盘子放到C柱
- 再将B柱上面的N-1个盘子放到C柱
比如,当上面 N-1 = 1的时候,即问题规模已经是2,则可以先将上面1个盘子
放到B,再将下面的盘子放到C柱,最后将B柱的1个盘子放到C柱,完成!
其实不管上面N-1是多少,不管上面有多少个盘子(>=1),即问题规模>=2的时候,都可以将其看作是1个盘子,
然后都是递归执行上述3步走策略。
直到问题规模为1的时候就可以直接将这1个盘子从A柱子移动到C
柱子,因此问题规模为1是递归退出的条件
还要注意的是:当A柱上面 N-1 个盘子放到 B柱的时候,下一步就要想先将B柱上面N-2个移到A
柱,再将B柱上最下面的放到C柱,然后将A柱N-2个盘子放到C柱。
因此这里的A、B、C三个柱子在代码中不能特指某个柱子,而应该表示- A:源柱子
- B:中间辅助柱子
- C:目的地柱子
-
代码思路分析
-
只要当前问题规模(盘子数num为1),就直接将盘子从源柱子A => 目的地柱子C
-
其他各种情况的问题规模(盘子数num>=2),都是
- 先将上面的num-1个盘子从源柱子A 借助目的地柱子C 移到辅助柱子B上
- 然后将最下面的1个盘子直接从源柱子A => 目的地柱子C
- 最后将辅助柱子B上的num-1个盘子借助源柱子A 移到目的柱子C上
就这样递归的执行
-
public class HanoiTower {
public static void main(String[] args) {
move(4, 'A', 'B', 'C');
}
// 汉诺塔的移动方法
// 使用分治算法
public static void move(int num, char a, char b, char c) {
// 如果只有1个盘
if(num == 1) {
System.out.println("第1个盘 " + a + " => " + c);
} else {
move(num - 1, a, c, b);
System.out.println("第" + num +"个盘 " + a + " => " + c);
move(num - 1, b, a, c);
}
}
}