在印度,有这么一个古老的传说:在世界中心贝拿勒斯(在印度北部)的圣庙里,一块黄铜板上插着三根宝石针。印度教的主神梵天在创造世界的时候,在其中一根针上从下到上地穿好了由大到小的64片金片,这就是所谓的汉诺塔。不论白天黑夜,总有一个僧侣在按照下面的法则移动这些金片,一次只移动一片,不管在哪根针上,小片必在大片上面。当所有的金片都从梵天穿好的那根针上移到另外一概针上时,世界就将在一声霹雳中消灭,梵塔、庙宇和众生都将同归于尽。
汉诺塔问题是一个经典的数学问题,也是计算机科学中常用于演示递归算法的示例之一。现在问题归纳如下:
问题:
有三根柱子(通常称为 A、B 和 C),初始时,在柱子 A 上有 n 个从上到下依次递减大小的圆盘。要求将这些圆盘从柱子 A 移动到柱子 C,每次只能移动一个圆盘,并且不能将较大的圆盘放在较小的圆盘上面。
目标:
将所有圆盘从柱子 A 移动到柱子 C,保持规则。
解题思路:
递归逻辑:
1.先把a的除最下面那个盘子意外的其他盘子移到b。为了实现这一步就得先把第一个移到c,第二个移到b,然后把c那个移到b。这时c就空了。
2.把a的最后一个移到空出来的c,这时a就空了
3.把b移到c,为了实现这一步就必须先把b上面那个移到空出来的a。再把下面那块移到c上面,最后把现在a上面那个,也就是最小那个放到c
这个是小学生都会玩的游戏😂
他的递归思想就是先把问题分成大三步,再在每个问题依次这么去分解,最后把所有大问题都解决掉,问题就ok了。跟怎么把大象赛冰箱里去的思路一样,第一步开门,第二步塞进去,第三步关门。最难的就是第二步赛进去,那就把第二步分解成小问题,依次解决掉,最后“塞进去”这个大问题就OK了,然后门一关就完事了。
上代码:
package main.java.honphie;
import java.util.LinkedList;
public class Hannoi {
static LinkedList<Integer> A = new LinkedList<>();
static LinkedList<Integer> B = new LinkedList<>();
static LinkedList<Integer> C = new LinkedList<>();
static void init(int n) {//创建1-n范围的数据
for (int i = n; i > 0; i--) {
A.addFirst(i);
}
}
/**
* @param n 盘子的数量
* @param a 源柱子
* @param b 中间柱子(借)
* @param c 目标柱子
*/
static void move(int n, LinkedList<Integer> a, LinkedList<Integer> b, LinkedList<Integer> c) {
if (n == 0) {
return;
}
move(n - 1, a, c, b);//把A柱子上的n-1个盘子移动到B柱子上(a借b移到c)
c.addFirst(a.removeFirst());//把A柱子上的最后一个盘子移动到C柱子上
System.out.println("n="+n);
print();
move(n - 1, b, a, c);//把B柱子上的n-1个盘子移动到C柱子上(b借a移到c)
//所需执行时间:T(n) = 2T(n-1)+c
//推导为:T(n) = c(2^n - 1)
//时间复杂度:Θ(2^n)
}
public Hannoi(int n) {
init(n);
print();
long start = System.nanoTime();
hannoi(n, A, B, C);
if(n==1){//通过单次挪动时间和时间复杂度求挪完64块所需的时间
long end = System.nanoTime();
double time = (end - start) / Math.pow(10,6);
System.out.println("单次移到耗时: " + time + "ms");
long ms=(long) (time*Math.pow(2,64));
long year= ms/(1000*60*60*24*365);
System.out.println("挪完64片预计需要: " + ms + "ms, 约为" + year + "年");
}
}
private static void print() {
System.out.println("A: " + A);
System.out.println("B: " + B);
System.out.println("C: " + C);
System.out.println("-------------");
}
}
n=3,执行结果:
顺便计算一下,不考虑栈内存的情况下,这个程序要执行多久:
输入n=1;
啊,要 62亿多年,假如僧人以流水线工人的手速挪动,就一秒钟一块的话,日夜不停,也需要大约10*10^3年,即一万亿年多。