汉诺塔
汉诺塔:汉诺塔(又称河内塔)问题是源于印度的一个古老传说。大梵天创造世界的时候做了三根金刚石柱子,在一根柱子上从下往上按照大小顺序摞着64片黄金圆盘。大梵天命令婆罗门把圆盘从下面开始按大小顺序重新摆放在另一根柱子上。并且规定,在小圆盘上不能放大圆盘,在三根柱子之间一次只能移动一个圆盘。
此处为了方便讲解,此处只拿3个圆盘做例子。
假设我们需要把A处的盘子全部挪到C上:
当盘子个数为1时:
1号盘子:A—>C
共移动1次
当盘子个数为2时:
1号盘子:A—>B
2号盘子:A—>C
1号盘子:B—>C
共移动3次
当盘子个数为3时:
1号盘子:A—>C
2号盘子:A—>B
1号盘子:C—>B
3号盘子:A—>C
1号盘子:B—>A
2号盘子:B—>C
1号盘子:A—>C
共移动7次
移动次数与盘子数量之间的规律
由上方规律不难得出,假设盘子数量为n,移动次数 = 2^n - 1
而该传说要求挪64个黄金圆盘,那么移动的次数就是 2^64 - 1 = 18446744073709511614(次)
假如每秒钟一次,那么一共需多长时间呢?一个平年365天有31536000 秒,闰年366天有31622400秒,平均每年31557600秒。
移完的话一共需要5845.42亿年以上!
盘子的移动规律
假设有n个盘子(n的大小代表盘子的大小)要将所有盘子由A移动到C,
根据上方的三个例子,不难发现,最大的第n号盘子永远只需要移动一次,而那一次是第(n + 1)/ 2次。
而在移动最大的盘子之前我们在做什么?我们在将(n - 1)个盘子由A移动到B。
移动完最大的盘子,我们又将(n - 1)个盘子由B移动到C。
进而我们可以将移动规律拆成3部分:
(1)前(n - 1)个盘子由A移动到B
(2)第n个盘子由A移动到C
(3)前(n - 1)个盘子由B移动到C
根据递归法,我们需要将复杂情况不断向基础情况靠拢,而基础情况便是当n = 1时,我们只需将盘子由A移动到C。
具体代码实现:
package com.shaocoder.towerofhanoi;
import java.util.Scanner;
public class TowerOfHanoi {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
System.out.print("请输入盘子的个数:");
int num = scan.nextInt();
hanoi(num,'A','B','C');
}
public static void hanoi(int num,char A,char B,char C) {
if(num == 1) {
System.out.println("移动盘子: " + num + " 从 " + A + " --> " + C);
}else{
hanoi(num - 1,A,C,B);
System.out.println("移动盘子: " + num + " 从 " + A + " --> " + C);
hanoi(num - 1,B,A,C);
}
}
}
运算结果如下
易混淆点
写求解汉诺塔的递归算法,有一点很容易被搞蒙,就是A,B,C三个点,不是说基础情况是盘子1由A移动到C吗?为什么有时候是A移动到C先,有时候是A移动到B先。此处我们不能把A,B,C三个点看死了。
如上述代码
hanoi(int num,char A,char B,char C)
A,B,C可以理解为起始点,辅助点,终点。
如果我们是想把盘子由A移动到B,那么我们调用的时候,B应该写在char C的位置上,中间的char B就为辅助点,用来辅助你去挪动这些盘子。
例:hanoi(num - 1,A,C,B);
总结
汉诺塔问题虽然用到了递归,但是这个问题的递归,一般思考进去几层,就会被完全的绕晕,可是我们其实并不需要去思考它内部是如何递归,也不管那个A,B,C三个位置的不断替换,我们只要了解这个汉诺塔的核心交换规律,几行代码,剩下的交给计算机去吧~