【栈与队列】求解汉诺塔问题(2.用栈非递归的方式)

功能需求(栈非递归的方式)

事先声明:博主在一本算法书上看到这个问题,对此有一些想法,有一部分出自抄腾,博主一心想表达自己对于处理问题的观点.对于此无需注明转发出处.此汉诺塔问题递归算法并未解决柱子还原之前不能为空问题,此种方法还有待优化.

        汉诺塔问题一直是数据算法结构中比较经典的一个问题,但是还需要略微解释一下:相传在古印度圣庙中,有一种被称为汉诺塔(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;
    }
}

亲测有效:内容无法复制

    原文作者: 汉诺塔问题
    原文地址: https://blog.csdn.net/hekaikai666/article/details/82529856
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞