栈和队列----用栈求解汉诺塔问题

用栈求解汉诺塔问题

  

  汉诺塔问题比较经典,现在修改一下汉诺塔游戏的规则:规定不能直接从左移动到右,也不能直接从右移动到左,必须经过中柱,求解当塔有N层的时候,打印最优移动过程和最优移动总步数。

  可以采用两个方法解决。方法一采用递归的方法;方法二采用非递归的方法,用栈来模拟汉诺塔的三个塔。

 

package com.test;

import java.util.Stack;

/**
 * Created by Demrystv.
 */
public class SolveHanoiWithStack {

    //递归的方法,左->中,右->中,中->左,中->右,都是需要三个步骤,左->右,右->左,都是需要五个步骤
    public int hanoiProblem1(int num, String left, String mid, String right){
        if (num < 1){
            return 0;
        }
        return process(num, left, mid, right, left, right);
    }

    private 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)){
            String another = (from.equals(left)) || (to.equals(left)) ? right : left;
            int part1 = process(num-1, left, mid, right, from, another);
            int part2 = 1;
            System.out.println("Move " + num + " from " + from + " to " + another);
            int part3 = process(num-1, left, mid, right, another, to);
            return part1 + part2 + part3;
        }else {
            int part1 = process(num-1, left, mid, right, from, to);
            int part2 = 1;
            System.out.println("Move " + num + " from " + from + " to " + mid);
            int part3 = process(num-1, left, mid, right, to, from);
            int part4 = 1;
            System.out.println("Move " + num + " from " + mid + " to " + to);
            int part5 = process(num-1, left, mid, right, from, to);
            return part1 + part2 + part3 + part4 + part5;
        }
    }



    //非递归方法用到的枚举类,因为不能直接从左到右,所以就只有四个动作
    public enum Action{
        No, LToM, MToL, MToR, RToM
    }

    //非递归的方法,用栈来模拟整个过程
    //核心思想:
    // 需要遵守两个原则,
    // 一个是不能违反小压大的原则,from栈弹出的元素num想压入到to栈中,那么num的值必须小于等于当前to栈的栈顶
    // 另一个是相邻不可逆原则,即不能上一步是左到中,下一步是中到左,不满足最小步数
    // 由这两个原则可以推出非递归方法的两个结论:
    // 1.游戏的第一个动作一定是 左 到 中
    // 2.四个动作中只有一个动作和相邻不可逆原则,其余三个一定都会违反,可以证明
    // 因此,每一步只有一个动作达标,那么只要每一步都根据这两个原则考察所有的动作即可,哪个满足就进行哪个动作,按照顺序走下来即可。
    public int hanoiProblem2(int num, String left, String mid, String right){
        Stack<Integer> lS = new Stack<Integer>();
        Stack<Integer> mS = new Stack<Integer>();
        Stack<Integer> rS = new Stack<Integer>();
        lS.push(Integer.MAX_VALUE);
        mS.push(Integer.MAX_VALUE);
        rS.push(Integer.MAX_VALUE);
        for (int i=num; i>0; i--){
            lS.push(i);
        }

        Action[] record = {Action.No};
        int step = 0;

        while (rS.size() != num + 1){
            step += fStackToStack(record, Action.MToL, Action.LToM, lS, mS, left, mid);
            step += fStackToStack(record, Action.LToM, Action.MToL, mS, lS, mid, left);
            step += fStackToStack(record, Action.RToM, Action.MToR, mS, rS, mid, right);
            step += fStackToStack(record, Action.MToR, Action.RToM, rS, mS, right, mid);
        }
        return step;
    }
    private int fStackToStack(Action[] record, Action preNoAct, Action nowAct, Stack<Integer> fStack, Stack<Integer> tStack,
                              String from, String to){
        if(record[0] != preNoAct && fStack.peek() < tStack.peek()){
            tStack.push(fStack.pop());
            System.out.println("Move " + tStack.peek() + " from " + from + " to " + to);
            record[0] = nowAct;
            return 1;
        }
        return 0;
    }
}

 

posted @ 2018-07-11 09:20  Demrystv  阅读(441)  评论(0编辑  收藏  举报