我在我的前几篇博客中谈到栈的数据结构,由于栈的先进后出的特点,使得她这中数据结构非常适合使用汉诺塔来解释,因为二者的操作原理一样。由此也衍生了针对汉诺塔的一些算法。其中一个就是三根柱子的汉诺塔的移动步骤问题。要求写出算法实现,给定初始的一根柱子上的圆盘个数,打印完成汉诺塔移动的实现步骤。
汉诺塔的原理
应该有许多同学对汉诺塔的规则都很了解,为了让部分不太了解汉诺塔的同学,这里我再详细地介绍一个汉诺塔的原理:假设有三根柱子分别为A B C,一开始A上有N个圆盘,从小到大、从上到下分别是1、2、3、4.....N-1、N,我们要把A上的N个圆盘全部移动到C上面,使得最后C上的圆盘的排列也是从小到大依次排下来,且每次移动梅根柱子最上面的一个圆盘。
思考:
那么要如何来解决这个问题呢?我相信大部分人都有一致的解决办法,若要把A上面的圆盘全部移动到C上,那么需要把N-1个圆盘按顺序落在B上,最后的第N个圆盘才可以直接从A移动到C上,从而完成第N个圆盘的移动。之后再把B上的N-2个圆盘移动到A上,再把B上剩下的最后一个圆盘,也就是第N-1个圆盘直接移到C上,这时就已经完成了最下面两个圆盘的移动。继续这样的移动直到完成游戏。
代码实现
上面的步骤在代码上使用递归是最好实现的。要使用递归就要考虑需要进行递归的是那一部分。
我们先看代码实现,然后结合代码具体分析这样写的原因
package cn.csu;
public class HanioTest {
/**
*
* @param n 柱子上需要移动圆盘的个数
* @param A 柱子A
* @param B 柱子B
* @param C 柱子C
*/
public static void hanoi(int n,char A,char B,char C){
if(n==1){
//只有一个圆盘需要移动,直接移动即可结束操作
move(A,C);
return;
}
//把A上的n-1个圆盘移动到B上
hanoi(n-1,A,C,B);
//把A上最后一个圆盘移到C上
move(A,C);
//接下来递归,把B上的n-1个圆盘移动到C上
hanoi(n-1,B,A,C);
}
/**
* 把A最上面的圆盘移动到C上
* @param A 柱子A
* @param C 柱子C
*/
public static void move(char A,char C){
System.out.println(A + "-->"+C);
}
public static void main(String[] args){
hanoi(3,'A','B','C');
}
}
我们对着代码来分析一下:
hanoi函数第一个参数是柱子上需要移动圆盘的个数,后三个参数分别为三个柱子的标识。首先n为1时,需要移动的圆盘只有一个,直接把A上的圆盘移动到C上就可以了,代码结束
接下来是汉诺塔实现的关键,即把A上所有的圆盘移动到C上,需要先把A最上面的n-1个圆盘移动到B上,于是有了hanoi(n-1,A,C,B)的递归操作,接下来只需要把A上剩下的最大的圆盘移到C上。
现在B上有n-1个圆盘,C有一个最大的圆盘,接下来把B上这n-1个圆盘也移动到C商,此时把B想象成为之前的A,有一堆待移动的圆盘,把A想象成之前的B,是空的柱子,这时我们把需要调用方式变为“hanoi(n-1,B,A,C)”就可以完成移动。这就是递归调用的思想所在了。