(经典问题之所以经典,是因为我们可以通过它学会解决一系列问题的思路,而不是仅仅只记住一个标准答案。)
汉诺塔(又称河内塔)问题是源于印度一个古老传说的益智玩具。大梵天创造世界的时候做了三根金刚石柱子,在一根柱子上从下往上按照大小顺序摞着64片黄金圆盘。大梵天命令婆罗门把圆盘从下面开始按大小顺序重新摆放在另一根柱子上。并且规定,在小圆盘上不能放大圆盘,在三根柱子之间一次只能移动一个圆盘。
分析过程:数学归纳法
当n = 1的时候,直接将 盘1 从 柱1 移至 柱3;
当n = 2的时候:
(1)盘1 移至 柱2;
(2)盘2 移至 柱3;
(3)盘1 移至 柱3;
当n = 3的时候:
(1)通过上面的步骤,将盘1和2移至柱2。(上面是移至柱3,但是不影响,因为柱子是等价的)。
(2)将盘3移至柱3。这个时候柱1为空,柱2有盘1和2,柱3有盘3,问题又变成了等价于与n=2的情形。
(3)重复步骤(1)。
当n=4的时候:
(1)通过上面的步骤,将盘1、2和3移至柱2。
(2)将盘4移至柱3。
(3)重复步骤(1)将盘1、2和3移至柱3。
……
相信看到这里,你已经想到了,这是一个递归的过程。不论n有多大,我们总能实现。这个递归写起来并不复杂,只是题目逻辑实现起来有点复杂而已。
代码
package test1;
import java.util.Scanner;
import java.util.Stack;
public class HanoiTower {
// 存放盘子
public Stack<Integer> disks;
// 塔柱的编号
public int index;
public HanoiTower(int i) {
disks = new Stack<>();
index =i;
}
/**
* 返回塔柱的编号
*/
public int index() {
return index;
}
/**
* 实现递归逻辑的方法,完成移动盘子的任务
* int n: 初始盘子的数量;
* HanoiTower t0: 初始放盘子的塔柱
* HanoiTower t2:目标塔柱;
* HanoiTower buffer:中间过渡的塔柱。
*/
public void moveDisks(int n, HanoiTower t0, HanoiTower t2, HanoiTower buffer) {
// 首先规定终止条件
if(n <= 0){
return;
}
System.out.printf("n = %d; Tower%d(num:%d) --> Tower%d(num:%d)\n", n, t0.index, t0.disks.size(), t2.index,
t2.disks.size());
// 先将t0上的1--n-1个盘子移到buffer上
moveDisks(n - 1, t0, buffer, t2);
// 这时,t0上还剩1个编号为n的盘子;buffer上有1--n-1,t2为空
// 将t0上的盘子移到t2
moveOne(t0, t2);
// 这时,t0为空;buffer上有1--n-1;t2有盘n
// 以t0为中转,将buffer上的盘子移到t2
moveDisks(n-1, buffer, t2, t0);
}
private void moveOne(HanoiTower t0, HanoiTower t2) {
if(t0.disks.isEmpty()){
System.err.println("The stack is empty!");
return;
}else {
int disk = t0.disks.pop();
t2.disks.push(disk);
}
}
public static void main(String[] args) {
System.out.print("请输入初始黄金盘的数量:");
Scanner scan = new Scanner(System.in);
int num = scan.nextInt();
// 新建三个塔柱
HanoiTower[] t = new HanoiTower[3];
for (int i = 0; i < 3; i++) {
t[i] = new HanoiTower(i);
}
// 往第一个塔柱上面放黄金盘,大的在下,小的在上
for (int i = num; i > 0; i--) {
t[0].disks.push(i);
}
t[0].moveDisks(num, t[0], t[2], t[1]);
System.out.printf("\nThe result is: \n Tower0(num:%d), Tower1(num:%d),Tower2(num:%d)", t[0].disks.size(),
t[1].disks.size(), t[2].disks.size());
}
}
如果对于代码的原理的理解还不够清楚,可以再仔细研究一下下面的测试输出。
测试
n = 2
请输入初始黄金盘的数量:2
n = 2; Tower0(num:2) --> Tower2(num:0)
n = 1; Tower0(num:2) --> Tower1(num:0)
n = 1; Tower1(num:1) --> Tower2(num:1)
The result is:
Tower0(num:0), Tower1(num:0),Tower2(num:2)
n=3
请输入初始黄金盘的数量:3
n = 3; Tower0(num:3) --> Tower2(num:0)
n = 2; Tower0(num:3) --> Tower1(num:0)
n = 1; Tower0(num:3) --> Tower2(num:0)
n = 1; Tower2(num:1) --> Tower1(num:1)
n = 2; Tower1(num:2) --> Tower2(num:1)
n = 1; Tower1(num:2) --> Tower0(num:0)
n = 1; Tower0(num:1) --> Tower2(num:2)
The result is:
Tower0(num:0), Tower1(num:0),Tower2(num:3)
n =4
请输入初始黄金盘的数量:4
n = 4; Tower0(num:4) --> Tower2(num:0)
n = 3; Tower0(num:4) --> Tower1(num:0)
n = 2; Tower0(num:4) --> Tower2(num:0)
n = 1; Tower0(num:4) --> Tower1(num:0)
n = 1; Tower1(num:1) --> Tower2(num:1)
n = 2; Tower2(num:2) --> Tower1(num:1)
n = 1; Tower2(num:2) --> Tower0(num:1)
n = 1; Tower0(num:2) --> Tower1(num:2)
n = 3; Tower1(num:3) --> Tower2(num:1)
n = 2; Tower1(num:3) --> Tower0(num:0)
n = 1; Tower1(num:3) --> Tower2(num:1)
n = 1; Tower2(num:2) --> Tower0(num:1)
n = 2; Tower0(num:2) --> Tower2(num:2)
n = 1; Tower0(num:2) --> Tower1(num:0)
n = 1; Tower1(num:1) --> Tower2(num:3)
The result is:
Tower0(num:0), Tower1(num:0),Tower2(num:4)