问题:
汉诺塔问题,要求移动时必须从中间经过(把三个盘子从左到右分别记为:L,M,R。也就是只有L->M,M->L,R->M,M->R四种动作)
分析:
1.递归:
按照经典汉诺塔递归思路,先把L柱子(左边柱子)看作两部分,第一部分为从最上面的盘子到底部倒数第二个盘子,第二部分为最下面的那个最大的盘子。
两部分为什么这样分?为什么不能把最上面那个小盘子作为第二部分?
因为最下面的盘子是最大的,所以其它所有的盘子都可以移动到这个盘子上面。
如果把最上面的小盘子作为第二部分,在移动它后,它上面是不能放盘子的(因为它面积最小),只有两个有效柱子是无法完成的。(而上面的相当于有三个柱子可以使用)
先把上面一个整体移动到M柱子(中间柱子),再把这个整体从M柱子移动到R柱子。然后把第二部分最大的那个盘子从L移动到M,再把第一部分的整体从R柱子移动到M柱子,再从M柱子移动到L柱子。把第二部分最大的那个盘子从M移动到R,把第一部分的整体从L柱子移动到M柱子,再从M柱子移动到R柱子。(用递归方法实现)
上面写的很绕,但其实就是分为两个部分,每个部分整体只能遵循LTOM,MTOL,MTOR,RTOM这样的规则走。
2.非递归:
非递归怎么模拟?
使用三个栈来模拟三个柱子,分别叫做stackL,stackM,stackR。
四个动作怎么定义?
用枚举变量enum来定义动作,分别为LTOM,MTOL,MTOR,RTOM。
明确:
1.第一步动作为LTOM(把L栈顶元素放到M栈中)
2.第一步动作走完后,以后每一步动作就已经固定。
为什么?
first:不可逆操作(如果上一步是LTOM,则下一步就不能是MTOL,否则来回移动,步数一定不是最优,或者说这样移动是无效的)
second:(面积)小的要压在大的上面(别忘记经典汉诺塔的规则,虽然把柱子用栈表示,但是上面的盘子依然有大小之分)
third:如果前一步为LTOM,则下一步不能为LTOM,因为下一步移动的元素(盘子的面积)是大于前一步的元素的(违反second)。下一步也不能为MTOL(违反first)。所以下一步只能从MTOR或RTOM中选择,而stackM和stackR各自栈顶的元素一个大一个小(面积),又因为second,所以就只能是其中一个动作。
如果前一步是其它的也是一样的道理,所以一旦第一步LTOM走出去,以后的每一步其实就已经确定了,所以代码实现只需要根据这条路一直走,直到所有的盘子放到stackR中。
解决方案:
1.递归:
使用递归方法hanoiOnlyThroughMid方法来做:
public void hanoiOnlyThroughMid(int n,char from,char buffer,char to)
n为盘子个数,from,buffer,to表示三个柱子,也可用String来做。
第二部分一个面积最大盘子的移动,其实表现就是一个printlin语句,因为它已经是一步可以做到的,就不用递归了。
step作为成员变量来计数。
刚开始为n,最后都递归到n等于1,也先输出,所以印证顺序是从上到下为1 2 … n。而除了最上面盘子的移动(n==1),剩下的输出都是靠后两个printlin语句。
实现代码为:
//类比普通汉诺塔规则
public class HanoiOnlyThroughMid {
private int step = 0;
public void hanoiOnlyThroughMid(int n,char from,char buffer,char to){
if(n == 1){
//不论怎么,n等于1的时候都是从from到to
System.out.println("Move " + n + " from " + from + " to " + to)