一、原理
河内之塔(Towers of Hanoi)是法国人M.Claus(Lucas)于1883年从泰国带至法国的,河内为越战时北越的首都,即现在的胡志明市。1883年法国数学家 Edouard Lucas曾提及这个故事,据说创世纪时Benares有一座波罗教塔,是由三支钻石棒(Pag)所支撑,开始时神在第一根棒上放置64个由上至下依由小至大排列的金盘(Disk),并命令僧侣将所有的金盘从第一根石棒移至第三根石棒,且搬运过程中遵守大盘子在小盘子之下的原则,若每日仅搬一个盘子,则当盘子全数搬运完毕之时,此塔将毁损,而也就是世界末日来临之时。
解法:
案例1 - 假设只有一个盘子的时候,就只有一个步骤,将第1个盘子从A移动到C。
步骤 盘子编号 源柱子 目的柱子
1 1 A C *
案例2 - 如果有两个盘子。
步骤 盘子编号 源柱子 目的柱子
1 1 A B
2 2 A C *
3 1 B C
案例 3 - 如果有三个盘子。
步骤 盘子编号 源柱子 目的柱子
1 1 A C
2 2 A B
3 1 C B
4 3 A C *
5 1 B A
6 2 B C
7 1 A C
现在开始找规律,我们要做的最重要的一件事情就是永远要把最底下的一个盘子从A移动到C,这个A和C不是固定不变的,有时我们需要将B看成C,有时需要将B看成A。看看上面从1个盘子的移动到3个盘子的移动,在移动记录中,当盘子的编号和盘子数量相同的时候,他们的步骤都是从A移动到C (看'*'标记的部分),其它的步骤对等。
再观察第3个案例中的第1-3步和第5-7步,第1-3步目的是从A移动到B,如果我们把B当作终点,那么这里的第1-3步理解起来和第2个案例的三个步骤完全相同,都是通过一个柱子来移动,和第2个案例比起来在后面加括号来表示:
1 1 A C (A -> B)
2 2 A B (A -> C)
3 1 C B (B -> C)
总结:将柱子B看成C,将C看成中转柱子。
第5-7步目的是从B移动到C如果我们把C当作终点,那么这里的5-7步理解起来和上面也是一样的,和第2个案例的三个步骤也完全相同。和第2个案例比起来就是:
5 1 B A (A -> B)
6 2 B C (A -> C)
7 1 A C (B -> C)
总结: 将柱子B看成A,将A看成中转柱子。
根据这个演示可以明确几点规律:
1. 当盘子只有一个的时候,只有一个动作,从A移动到C即结束。
2. 当有N个盘子的时候,中间的动作都是从A移动到C,那么表示最下面的第N个盘子移动完毕。
3. 中间动作之上都可以认为是:从A移动到B,C做中转。
4. 中间动作之下都可以认为是:从B移动到C,A做中转。
2,3,4 可以表示为:
1 1 A B
2 2 A C
3 1 B C
这种结构一直在重复进行。
至于代码中第1个参数为什么是DiskQuantity – 1,或者1。大家再回到上面看看是不是所有的步骤都是:
[ 1 ] [ 1,2,1 ] [ 1,2,1,3,1,2,1 ]
这种以盘子数对称的结构,而它前后都是重复1,2,1的过程。
二、Java源码
class HanoiTower
{
public void MoveDisk(int N, char A, char B, char C)
{
// If there's only one disk, then end.
if (1 == N)
{
System.out.println(String.format("Move disk from position %c to %c", A, C));
}
else
{
// Step 1 - Change B to C
MoveDisk(N - 1, A, C, B);
// Step 2 - No changes
MoveDisk(1, A, B, C);
// Step 3 - Change B to A
MoveDisk(N - 1, B, A, C);
}
}
public static void main(String[] args)
{
HanoiTower hanoi = new HanoiTower();
int DiskQuantity = 3;
hanoi.MoveDisk(DiskQuantity, 'A', 'B', 'C');
}
}
输出结果:
DNFM0019(9)