汉诺塔问题

package com.xy;

import java.util.Stack;

/**
 * 汉诺塔问题,计算步数和具体流程,规定不能直接从左边到右边或右边到左边。
 */
public class HanoiProblem {
    public static void main(String[] args) {
//        System.out.println(hanoi1(3, "left", "mid", "right"));
        System.out.println(hanoi2(3, "left", "mid", "right"));
    }

    /**
     * 解法一:递归解法
     *
     * @param num 盘数量
     * @param left 左边柱描述
     * @param mid 中间柱描述
     * @param right 右边柱描述
     * @return 移动次数
     */
    public static int hanoi1(int num, String left, String mid, String right) {
        if (num < 1) {
            return 0;
        }
        return process(num, left, mid, right, left, right);
    }

    private static int process(int num, String left, String mid, String right, String from, String to) {
        if (num == 1) {//递归终止条件
            if (from.equals(mid) || to.equals(mid)) {
                System.out.println("Move 1 from " + from + " to " + to);
                return 1;
            } else {
                System.out.println("Move 1 from " + from + " to " + mid);
                System.out.println("Move 1 from " + mid + " to " + to);
                return 2;
            }
        }
        if (from.equals(mid) || to.equals(mid)) { 
            //移动的一方为中间时的操作,分为三步
            //1、将n-1的盘子移动到无关柱上(即除出发地和目的地的柱子)
            //2、将第n个盘子移动到目的地柱子
            //3、将n-1的盘子移动到目的地柱子
            String anothor = (from.equals(left) || to.equals(left)) ? right : left;
            int part1 = process(num - 1, left, mid, right, from, anothor);
            System.out.println("Move " + num + " from " + from + " to " + to);
            int part2 = 1;
            int part3 = process(num - 1, left, mid, right, anothor, to);
            return part1 + part2 + part3;
        } else {
            //移动出发地和目的地是两边时分为5步
            //1、将n-1的盘子移动到目的地
            //2、将第n个盘子移动到中间柱子
            //3、将n-1的盘子移动到出发地
            //4、将第n个盘子移动到目的地
            //5、将n-1个盘子移动到目的地
            int part1 = process(num - 1, left, mid, right, from, to);
            System.out.println("Move " + num + " from " + from + " to " + mid);
            int part2 = 1;
            int part3 = process(num - 1, left, mid, right, to, from);
            System.out.println("Move " + num + " form " + mid + " to " + to);
            int part4 = 1;
            int part5 = process(num - 1, left, mid, right, from, to);
            return part1 + part2 + part3 + part4 + part5;
        }
    }

    /**
     * 解法二:使用栈结构。三个立柱可看成三个栈
     * @param num 塔、盘数量
     * @param left 左侧柱描述
     * @param mid 中间柱描述
     * @param right 右侧柱描述
     * @return 移动次数
     * 该方法基于每次盘子的移动只能有一个方向移动,可基于两条规则判断
     * 1、不走回路,即不能移动到刚移过来的出发地
     * 2、目的地盘子大于要移动的盘子
     */
    public static int hanoi2(int num,String left,String mid,String right) {
        Stack<Integer> leftStack = new Stack<>();
        Stack<Integer> midStack = new Stack<>();
        Stack<Integer> rightStack = new Stack<>();
        leftStack.push(Integer.MAX_VALUE);
        midStack.push(Integer.MAX_VALUE);
        rightStack.push(Integer.MAX_VALUE);
        for(int i = num;i>0;i--){
            leftStack.push(i);
        }
        int step = 0;
        Action[] record = {Action.NO};
        while(rightStack.size() < num+1){
            //轮询每一次的移动,三根立柱,只有四个移动选项,查询哪个选项可以移动
            step += fStackTotStack(record,Action.MToL,Action.LToM,leftStack,midStack,left,mid);
            step += fStackTotStack(record,Action.LToM,Action.MToL,midStack,leftStack,mid,left);
            step += fStackTotStack(record,Action.MToR,Action.RToM,rightStack,midStack,right,mid);
            step += fStackTotStack(record,Action.RToM,Action.MToR,midStack,rightStack,mid,right);
        }
        return step;
    }

    private static int fStackTotStack(Action[] record, Action preAction, Action nowAction, Stack<Integer> fStack, Stack<Integer> tStack, String from, String to) {
        //满足两条规则才移动
        if(record[0]!=preAction && fStack.peek() < tStack.peek()) {
            tStack.push(fStack.pop());
            record[0] = nowAction;
            System.out.println("Move " + tStack.peek() + " from " + from + " to " + to);
            return 1;
        }else {
            return 0;
        }
    }
}
//构造枚举类来判断是否有回路
enum Action {
    NO,LToM,MToL,MToR,RToM
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值