功能需求(栈非递归的方式)
事先声明:博主在一本算法书上看到这个问题,对此有一些想法,有一部分出自抄腾,博主一心想表达自己对于处理问题的观点.对于此无需注明转发出处.此汉诺塔问题递归算法并未解决柱子还原之前不能为空问题,此种方法还有待优化.
汉诺塔问题一直是数据算法结构中比较经典的一个问题,但是还需要略微解释一下:相传在古印度圣庙中,有一种被称为汉诺塔(Hanoi)的游戏。该游戏是在一块铜板装置上,有三根杆(编号A、B、C),在A杆自下而上、由大到小按顺序放置64个金盘(如下图)。游戏的目标:把A杆上的金盘全部移到C杆上,并仍保持原有顺序叠好。操作规则:每次只能移动一个盘子,并且在移动过程中三根杆上都始终保持大盘在下,小盘在上,操作过程中盘子可以置于A、B、C任一杆上。在这里我们来修改一下游戏规则:现在限制不能从最左侧的塔直接移动到最右侧,也不能从最右侧的塔直接移动到最左侧,而必须经过中间。但当求N层的时候,打印最优的移动过程和最优的移动总步数。
例如:当塔层数为两层时,最上层的塔标记为1,最下层的塔标记为2,这打印:
Move 1 from left to mid
Move 1 from mid to right
Move 2 from left to mid
Move 1 from right to mid
Move 1 from mid to left
Move 2 from mid to right
Move 1 from left to mid
Move 1 from mid to right
It will move 8 steps。
要求:可以使用递归和非递归两种方法做,非递归算法用栈来模拟汉诺塔的三个塔。
详细解析:
方法二:非递归方法—-用栈的方式来模拟整个过程.
修改后的汉诺塔问题不能从任何塔从”左”直接移动到右,也不能从右直接移动到左,必须经过中间.那么我们所有的操作就会变成四个动作:”左”到”中”,”中”到”右”,”右”到”中”,”中”到”左”.
现在 我们需要把这种操作来抽象成栈来进行操作,那么左中右三个栈可以依次标记为LS,MS,RS.最初所有的塔都在LS上.那么 如果上四个动作就会变成:某一个栈(from)吧栈顶元素弹出,然后压入另一个栈中(to),这作为这个栈(to)的栈顶.但是我们需要考虑的是存入栈的先决条件(不违反小压大的规则)form栈弹出的元素num如果想要压入到to栈中,那么num的值必须小于当前to栈的栈顶.还有一个原则不是很明显,但是很致命,也非常中邮重要,相邻不可逆原则.这个原则我们需要详细解释一下:
1.我们把四个动作可以依次定义为L → M,M → L,R → M,和R → M
2.可以明显的看出L → M和M → L是互为相反的,其他两组也一致如此,所以他们具有相互逆的过程.
3.如果想要走出最少的步骤,任何两个相邻的动作都是不可逆的. 有了大压小和相邻不可逆的原则后,可以推导出两个核心结论
1>这个游戏的第一步一定是L → M
2>在走出最少步数的过程中的每个时刻,四个动作不有一个动作不违反小压大和相邻不可逆的原则:这里不再做过多详细的解释
假设前一步动作是L → M
1.根据小压大的原则,M → L的动作不会重复
2.根据相邻不可逆的原则,M → L不可能发生
3.根据小压大的原则,M → R和R → M只会有一个达标
其余三种 M → L,M → R, R → M同样的原则.
根据以上的分析和理解:每一步都只有一个动作达标.那么只要每走一步根据这两个原则考察所有动作即可,哪个动作标准就走哪个动作.按顺序走下来.
非递归的具体步骤请看如下代码:
package com.hekaikai666.test3;
/**
*
* @author hekaikai666
* @time 2018年9月20日下午7:53:23
**/
public enum Action {
No,LToM,MToL,MtoR,RToM
}
package com.hekaikai666.test3;
import java.util.Stack;
/**
*
* @author hekaikai666
* @time 2018年9月20日下午7:54:39
**/
public class hanoiProblem2 {
public int hanoiProblem(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 += fromStackTotoStack(record, Action.MToL, Action.LToM, lS, mS, left, mid);
step += fromStackTotoStack(record, Action.LToM, Action.MToL, mS, lS, mid, left);
step += fromStackTotoStack(record, Action.RToM, Action.MtoR, mS, rS, mid, right);
step += fromStackTotoStack(record, Action.MtoR, Action.RToM, rS, mS, right, mid);
}
return step;
}
public static int fromStackTotoStack(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;
}
}
亲测有效:内容无法复制