6.用栈来求解汉诺塔问题

本文介绍了如何使用递归和非递归方式解决汉诺塔问题。递归方法通过将问题分解为较小部分,遵循特定规则进行移动。非递归方法利用栈模拟柱子,通过枚举变量定义动作,并通过while循环按预设路径完成所有移动。
摘要由CSDN通过智能技术生成

问题:

汉诺塔问题,要求移动时必须从中间经过(把三个盘子从左到右分别记为: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)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值