算法-汉诺塔问题及递归 详细解释 Java版

算法-汉诺塔问题及递归 Java

1. 问题描述

相传在古印度圣庙中,有一种被称为汉诺塔(Hanoi)的游戏。该游戏是在一块铜板装置上,有三根杆(编号A、B、C),在A杆自下而上、由大到小按顺序放置64个金盘。游戏的目标:把A杆上的金盘全部移到C杆上,并仍保持原有顺序叠好。操作规则:每次只能移动一个盘子,并且在移动过程中三根杆上都始终保持大盘在下,小盘在上,操作过程中盘子可以置于A、B、C任一杆上。

2. 算法逻辑

前提:默认是最底下、最大的圆盘是第n个圆盘,设n远大于1。

(注:下面所说的都是想法,而不是具体实现,不要在看到这些文字时就代入代码里面,而是先理解这些文字的意思!)

要想完成整个移动,我们可以这样子想:

  1. 首先将第n个圆盘和上面的n-1个圆盘分别看做一个整体,首先我们要做的就是把上面的“n-1个圆盘”(这里说的是一个整体)移动到辅助柱上。
  2. 其次把第n个圆盘移动到目标柱上,到这一步就完成了最大的圆盘移动到了目标柱的最下面。
  3. 最后要把辅助柱上的n-1个圆盘(这里同样看做一个整体)移动到目标柱,这样就完成了所有移动。(这里要注意的是,因为从辅助柱移动到目标柱,本身又可以看作是一个汉诺塔问题,所以原本的辅助柱在这里变成了源柱,原本的源柱在这里变成了辅助柱。)

可能有人就会有疑问了,“每次只能移动一个盘子,这里一下移动了n-1个盘子,明显不符合要求啊“,暂且保留这个问题,顺着思路继续向下走。

3. 算法实现

3.1 方法声明

定义方法move,用来实现递归算法,其中参数n表示圆盘的个数,a表示源柱,b表示辅助柱,c表示目录柱,算法最终实现“打印将源柱a上的圆盘移动到目标柱c上的所有步骤”。

/*
 * @param n 圆盘数量
 * @param a 源柱
 * @param b 辅助柱
 * @param c 目标柱
 */
public static void move(int n, char a, char b, char c) {
}

3.2 递归终止条件

当源柱上只有一个圆盘时,可以直接将源柱上的这个圆柱移动到目标柱上,这就是递归终止条件。

/*
 * @param n 圆盘数量
 * @param a 源柱,只是记录一个符号,没有实际含义
 * @param b 辅助柱,只是记录一个符号,没有实际含义
 * @param c 目标柱,只是记录一个符号,没有实际含义
 */
public static void move(int n, char a, char b, char c) {
    if (n == 1) {
        // 如果源柱上只有一个圆盘,直接将圆盘从源柱a上移动到目标柱c上
        System.out.printf("从%s上移动第%d个圆盘到%s上\n", a, n, c);
    } 
}

3.3 递归非终止条件

在递归的非终止条件中,将完成算法逻辑中所示的三个步骤

public static void move(int n, char a, char b, char c) {
    if (n == 1) {
        // 如果源柱上只有一个圆盘,直接将圆盘从源柱a上移动到目标柱c上
        System.out.printf("从%s上移动第%d个圆盘到%s上\n", a, n, c);
    } else {
		// 步骤1: 将 n - 1(从上至下)个圆盘,移动到辅助柱上
		move(n - 1, a, c, b);
		// 步骤2: 将第 n 个圆盘移动到目标柱上
        System.out.printf("从%s上移动第%d个圆盘到%s上\n", a, n, c);
        // 步骤3: 将 n - 1 个圆盘从辅助柱上移动到目标柱上,此时a作为辅助柱
         move(n - 1, b, a, c);
	}
}

4. 算法解析

4.1 步骤一解析

// 步骤1: 将 n - 1(从上至下)个圆盘,移动到辅助柱上
move(n - 1, a, c, b);
  1. 在移动n-1个圆盘时,由于使用的是递归算法,又因为n != 1,所以会再次调用该函数,此时 n = n - 1,我们且称之为 n1。
  2. 同样的在移动n1个圆盘时,n1 != 1,所以又调用该函数,此时 n = n1 -1,也即 n = n - 2,我们且称之为 n2。
  3. 为了更好的理解,这里再多分析一个步骤!在移动n2个圆盘时,n2!=1,所以又调用该函数,此时n=n2-1,即 n=n-3,我们且称之为n3。
  4. 直到递归调用n-1次后,此时n=n-(n-1)=1,满足递归终止条件,也就是在执行n-1次调用后,终于到了最上面的那个圆盘了,就可以开始移动了,直接将最上面的圆盘移动到“目标柱”上。注意注意这一步传入的“目标柱”是实际上调用方法时传入的辅助辅。

4.2 步骤二解析

 // 步骤2: 将第 n 个圆盘移动到目标柱上
System.out.printf("从%s上移动第%d个圆盘到%s上\n", a, n, c);
  1. 步骤一最顶层函数执行完成后,最上面n-(n-1)那个元素已经完成了移动。
  2. 最顶层函数步骤二执行,将n-(n-2)移动到另一根柱子上。

4.3 步骤3解析

 // 步骤3: 将 n - 1 个圆盘从辅助柱上移动到目标柱上,此时a作为辅助柱
 move(n - 1, b, a, c);
  1. 执行到这里时,n-(n-2)已经在最下面了,些时要做的就是把n-(n-1)再移动回去。

至此,一次函数回归分析完毕,后续执行类似。

5. 汉诺塔算法

public class Hanoi {
    public static void main(String[] args) {
        move(3, 'a', 'b', 'c');
    }
	
	/*
     * @param n 圆盘数量
     * @param a 源柱
     * @param b 辅助柱
     * @param c 目标柱
     */
    public static void move(int n, char a, char b, char c) {
        if (n == 1) {
            // 如果源柱上只有一个圆盘,直接从源柱a上移动到目标柱c上
            System.out.printf("从%s上移动第%d个圆盘到%s上\n", a, n, c);
        } else {
            // 步骤1: 将 n - 1(从上至下)个圆盘,移动到辅助柱上
            move(n - 1, a, c, b);
            // 步骤2: 将第 n 个圆盘移动到目标柱上
            System.out.printf("从%s上移动第%d个圆盘到%s上\n", a, n, c);
            // 步骤3: 将 n - 1 个圆盘从辅助柱上移动到目标柱上,此时a作为辅助柱
            move(n - 1, b, a, c);
        }
    }

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值