汉诺塔Java递归和非递归算法解析

汉诺塔Java递归和非递归算法解析

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

这是一个典型的递归调用算法,要实现把n个圆盘从A移到C,我们只需要做到如下三步:

①:把n-1个圆盘从A移到B

②:把A最底层的圆盘从A移到C

③:把B上的n-1个圆盘移到C。

public class Hanio递归 {
    static int i = 1;
    public static void main(String[] args) {
        Hanoi(3,"A","B","C");
    }
    public static void Hanoi(int num,String a,String b,String c){
        if (num ==1 ){
            System.out.println ("第  "+i+"  次移动,把编号为  1  的圆盘从  "+a+"   移动到  "+c);
            i++;
        }
        else {
            Hanoi(num - 1, a, c, b);
            // 将起始柱上剩余的最后一个大圆盘移动到目标柱上
            System.out.println("第  "+i+"  次移动,把编号为  "+num+"  的圆盘从  "+a+"   移动到  "+c);
            i++;
            // 递归调用 hanoi() 函数,将辅助柱上的 num-1 圆盘移动到目标柱上
            Hanoi(num - 1, b, a, c);
        }
    }
}

Hanoi他问题的递归算法时间复杂度是O(2^n),而且递归算法调用空间是很大的,代码难写但简洁,计算机非常难受。

那么非递归算法该怎么实现呢

最简单的就是用栈模拟递归

import java.util.Stack;

public class Hanoi非递归 {
    public static void main(String[] args) {
        Hanoi("A","B","C",3);
    }
    public static void Hanoi(String a,String b,String c,int num){
        Stack<State> starks = new Stack<> ();
        State state = new State(a,b,c,num,false);
        starks.push (state);
        while (starks.size ()>0){
            if(starks.peek ().title){
                HanoiPrint(starks.pop ());
            }
            else {
                State NumLinShi=starks.pop ();
                if (NumLinShi.Num==1){
                    HanoiPrint(NumLinShi);
                }
                else {
                    State NumLinA_B = new State (NumLinShi.A,NumLinShi.C,NumLinShi.B,NumLinShi.Num-1,false);
                    State NumLinA_C = new State (NumLinShi.A,NumLinShi.B,NumLinShi.C,NumLinShi.Num,true);
                    State NumLinB_C = new State (NumLinShi.B,NumLinShi.A,NumLinShi.C,NumLinShi.Num-1,false);
                    starks.push (NumLinB_C);
                    starks.push (NumLinA_C);
                    starks.push (NumLinA_B);
                }
            }
        }
    }
    public static void HanoiPrint(State str){
        System.out.println ("把编号为\t"+str.Num+"\t的圆盘从\t"+str.A+"\t移动到\t"+str.C+"\t盘子上");
    }
}
class State {
    public String A;
    public String B;
    public String C;
    public int Num;
    public boolean title;
    public State(String A, String B, String C , int Num,boolean title)
    {
        this.A = A;
        this.B = B;
        this.C = C;
        this.Num = Num;
        this.title = title;
    }
}

先定义一个类名叫State,State有属性A,B,C,Num,title,A就是盘子的移动杆,B就是中间杆,C就是盘子的接受杆,Num就是被移动的盘子编号,title是一个标记,当title=true时表示该对象可以输出了。

这个代码的核心是Hanoi非递归类中的Hanoi方法,我们来拆解这个方法。

首先定义一个栈,把原始数据存入

Stack<State> starks = new Stack<> ();
        State state = new State(a,b,c,num,false);

之后如果栈顶元素的Num=1,那就是只有一个盘子了就直接输出,

否则把栈顶元素按照如下规则一化三,栈顶元素出栈,以下三个元素进栈

①:把n-1个圆盘从A移到B

②:把A最底层的圆盘从A移到C

③:把B上的n-1个圆盘移到C。

while (starks.size ()>0){
    if(starks.peek ().title){
        HanoiPrint(starks.pop ());
    }
    else {
        State NumLinShi=starks.pop ();
        if (NumLinShi.Num==1){
            HanoiPrint(NumLinShi);
        }
        else {
            State NumLinA_B = new State (NumLinShi.A,NumLinShi.C,NumLinShi.B,NumLinShi.Num-1,false);
            State NumLinA_C = new State (NumLinShi.A,NumLinShi.B,NumLinShi.C,NumLinShi.Num,true);
            State NumLinB_C = new State (NumLinShi.B,NumLinShi.A,NumLinShi.C,NumLinShi.Num-1,false);
            starks.push (NumLinB_C);
            starks.push (NumLinA_C);
            starks.push (NumLinA_B);
        }
    }
}
   starks.push (NumLinB_C);
        starks.push (NumLinA_C);
        starks.push (NumLinA_B);
    }
}

}
在这里插入图片描述

  • 4
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Addam Holmes

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值