java算法系列,第五篇:汉诺塔问题

文章讲述了印度传说中的汉诺塔问题,它是递归算法的典型示例。通过Java代码实现汉诺塔移动圆盘的过程,分析了解题策略和时间复杂度为O(2^n)。最后计算了移动64片圆盘所需的时间,显示了其在实际中的巨大挑战。
摘要由CSDN通过智能技术生成

在印度,有这么一个古老的传说:在世界中心贝拿勒斯(在印度北部)的圣庙里,一块黄铜板上插着三根宝石针。印度教的主神梵天在创造世界的时候,在其中一根针上从下到上地穿好了由大到小的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年,即一万亿年多。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值