用栈求解汉诺塔问题
汉诺塔问题比较经典,现在修改一下汉诺塔游戏的规则:规定不能直接从左移动到右,也不能直接从右移动到左,必须经过中柱,求解当塔有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; } }