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
}
汉诺塔问题
于 2022-05-16 22:10:27 首次发布