汉诺塔问题

本文介绍了一个经典的递归问题——汉诺塔,并提供了详细的递归逻辑解析及Java实现代码。通过实例演示了如何运用递归思维解决汉诺塔问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

(经典问题之所以经典,是因为我们可以通过它学会解决一系列问题的思路,而不是仅仅只记住一个标准答案。)

汉诺塔(又称河内塔)问题是源于印度一个古老传说的益智玩具。大梵天创造世界的时候做了三根金刚石柱子,在一根柱子上从下往上按照大小顺序摞着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)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值