【算法题】06-用栈来解决汉诺塔问题

用栈来解决汉诺塔问题

题目

修改汉诺塔问题的游戏规则:限制不能从最左侧的塔直接移动到最右侧,也不能从最右侧直接移动到最左侧,而是必须经过中间。求当塔有N层的时候,打印最优移动和最优移动总步数。

要求:

  • 方法一:递归的方法
  • 方法二:非递归的方法,用栈来模拟汉诺塔的三个塔

思路

方法一:递归的方法

首先,如果只剩最上层的塔需要移动,则有如下处理:

  1. 如果希望从左移动到右,打印Move 1 from left to right
  2. 如果希望从中移动到左,打印Move 1 from mid to left
  3. 如果希望从中移动到右,打印Move 1 from mid to right
  4. 如果希望从右移动到中,打印Move 1 from right to mid
  5. 如果希望从左移动到右,打印Move 1 from left to mid Move 1 from mid to right
  6. 如果希望从右移动到左,打印Move 1 from right to mid Move 1 from mid to left

以上就是递归的终止条件,也就是只剩上层塔时的打印过程。

多层塔的时候。

如果剩下N层塔,从最上到最小依次为1~N-1,则有如下判断

  1. 如果剩下的N层塔都在左,希望全部移到中,则有三个步骤

    1)将1~N-1层塔先全部从左移动到右,交给递归过程

    2)将第N层塔从左移到中

    3)将1~N-1层塔全部从右移到中,明显交给递归过程

  2. 如果把剩下的N层塔从中移到左,从中移到右,从右移到中过程与上述相同
  3. 如果剩下的N层都在左,希望全部移动到右。

    1)将1~N-1层塔先全部从左移动到右,交给递归

    2)将第N层塔从左移动到中

    3)将1~N-1层塔从右移到左

    4)N层从中移到右

    5)最后将1~N-1层塔全部从左移到右,交给递归过程

  4. 如果剩下全在右,希望移到左,同上
public static int hanoiProblem1(int num, String left, String mid,
            String right) {
        if (num < 1) {
            return 0;
        }
        return process(num, left, mid, right, left, right);
    }

    public static int process(int num, String left, String mid, String right,
            String from, String to) {
        //只有一层需要移动的时候
        if (num == 1) {
            //如果from或to有一个是mid,说明是从右往中,或从左往中,直接移动即可。
            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;
            }
        }
        //当层数大于1的时候,且有一个是到中间
        if (from.equals(mid) || to.equals(mid)) {
            //如果是从左到中,或者中到左 another就为右,否则就为左,就是说现在参与的是左中,那么剩下的一个是右,同理右中
            String another = (from.equals(left) || to.equals(left)) ? right : left;
            //递归处理n-1层,从当前的from到另一端,即从做到右,或从右到左
            int part1 = process(num - 1, left, mid, right, from, another);
            //将第N层塔从左移到中或从右移到中
            int part2 = 1;
            System.out.println("Move " + num + " from " + from + " to " + to);
            //将1~N-1层塔全部从右移到中,或者从左移动中
            int part3 = process(num - 1, left, mid, right, another, to);
            return part1 + part2 + part3;
        } else {
            //从左到右或者从右到左  递归处理n-1层
            //将1~N-1层塔先全部从左移动到右或从右移到左
            int part1 = process(num - 1, left, mid, right, from, to);
            //将第N层塔从左移动到中 或从右移到中
            int part2 = 1;
            System.out.println("Move " + num + " from " + from + " to " + mid);
            //将1~N-1层塔从右移到左 或相反
            int part3 = process(num - 1, left, mid, right, to, from);
            //N层从中移到右
            int part4 = 1;
            System.out.println("Move " + num + " from " + mid + " to " + to);
            //最后将1~N-1层塔全部从左移到右,交给递归过程
            int part5 = process(num - 1, left, mid, right, from, to);
            return part1 + part2 + part3 + part4 + part5;
        }
    }

方法二:非递归,用栈来模拟

将左、中、右三个地点抽象成栈,LS、MS、RS,那么栈的操作就可以看成是:某一个栈(from)把栈顶元素弹出,然后压入到另一个栈里(to),作为另一个栈(to)的栈顶。

  1. 游戏的第一个动作一定是L->M,这是显而易见的。
  2. 在走出最小部署过程中的任何时刻,四个动作只有一个动作不违反小压大和相邻不可逆原则,另外三个一定会违反

    上述第二点证明:

    假设前一步的动作是L->M:

    1. 根据小压大的原则,L->M的动作不会重复发生
    2. 根据相邻不可逆原则,M->L的动作也不该发生
    3. 根据小压大的原则,M->R和R->M只会有一个达标

    假设前一步的动作是M->L:

    1. 根据小压大原则,M->L的动作不会重复发生
    2. 根据相邻不可逆原则L->M也不会发生
    3. 根据小压大原则,M->R和R->M只会有一个达标

    假设前一步的动作是M->R:

    1. 根据小压大的原则,M->R不会重复发生
    2. 根据相邻不可逆原则,R->M也不会发生
    3. 根据小压大的原则,L->M和M->L只会有一个达标

    假设前一步的动作是R->M:

    1. 根据小压大的原则,R->M的动作不会重复发生
    2. 根据相邻不可你原则,M->R的动作也不该发生
    3. 根据小压大原则,L->M和M->L只会 有一个达标

    如上,每一步只会有一个动作达标,那么只要每走一步都根据这两个原则考察所有的动作就可以,哪个达标走哪个。

public static enum Action {
        No, LToM, MToL, MToR, RToM
    }

    public static 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);
        //从num开始由大到小依次入左栈
        for (int i = num; i > 0; i--) {
            lS.push(i);
        }
        Action[] record = { Action.No };
        int step = 0;
        //如果右栈的个数不等于num+1说明还没有移动完
        while (rS.size() != num + 1) {
            step += fStackTotStack(record, Action.MToL, Action.LToM, lS, mS, left, mid);
            step += fStackTotStack(record, Action.LToM, Action.MToL, mS, lS, mid, left);
            step += fStackTotStack(record, Action.RToM, Action.MToR, mS, rS, mid, right);
            step += fStackTotStack(record, Action.MToR, Action.RToM, rS, mS, right, mid);
        }
        return step;
    }

原文地址:https://www.cnblogs.com/dream-to-pku/p/12427509.html

时间: 2024-08-10 01:15:02

【算法题】06-用栈来解决汉诺塔问题的相关文章

Java编程用栈来求解汉诺塔问题的代码实例(非递归)_java - JAVA

文章来源:嗨学网 敏而好学论坛www.piaodoo.com 欢迎大家相互学习 [题目] 汉诺塔问题比较经典,这里修改一下游戏规则:现在限制不能从最左侧的塔直接移动到最右侧,也不能从最右侧直接移动到最左侧,而是必须经过中间.求当塔有N层的时候,打印最优移动过程和最优移动总步数. [解答] 上一篇用的是递归的方法解决这个问题,这里我们用栈来模拟汉诺塔的三个塔,也就是不用递归的方法 原理是这样的:修改后的汉诺塔问题不能让任何塔从左直接移动到右,也不能从右直接移动到左,而是要经过中间,也就是说,实际上

1.6 用栈来求解汉诺塔问题

题目:汉诺塔问题比较经典,这里修改一下游戏规则:现在限制不能从最左侧的塔直接移动到最右侧,也不能从最右侧直接移动到最左侧,而是必须经过中间.求当塔有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

编程:递归编程解决汉诺塔问题(用java实现)

//Li Cuiyun,October 14,2016.//用递归方法编程解决汉诺塔问题package tutorial_3_5;import java.util.*; public class HanoiTower { public static void main(String[] args) { // TODO Auto-generated method stub @SuppressWarnings("resource") Scanner sc=new Scanner(Syste

栈解决汉诺塔问题

汉诺塔问题比较经典,这里修改--下游戏规则: 现在限制不能从最左侧的塔直接移动到最右侧,也不能从最右侧直接移动到最左侧,而是必须经过中间. 求当塔有N层的时候,打印最优移动过程和最优移动总步数. 在走出最少步数过程中的任何时刻,四个动作中只有一个动作不违反小压大和相邻不可逆原则(相邻的两次操作不互为逆操作如:MtoR和RtoM),另外三个动作一定都会违反. #include <iostream> #include <stack> using namespace std; enum

【Python学习】Python解决汉诺塔问题

参考文章:http://www.cnblogs.com/dmego/p/5965835.html 一句话:学程序不是目的,理解就好:写代码也不是必然,省事最好:拿也好,查也好,解决问题就好! 信息时代不用信息就是罪过,直接抄不加理解与应用,就不是自己的,下次遇到还是不会,或许其中的某一个细节就能够用于各个问题的解决,共勉 学习一个东西总会遇到一些经典的问题,学习Python第二天尝试看一下汉诺塔问题,还是百度,看看解题思路,纯粹是重温初中课堂,越活越回去了 汉诺塔的图解递归算法 一.起源: 汉诺

用栈来求解汉诺塔问题

当然.这是一个经典的递归问题~   想必来看这篇博文的同学对汉诺塔应该不会陌生了吧, 写这篇博还是有初衷的: 之前学数据结构的时候自己看书.也上网上查了很多资料,资料都比较散.而且描述的不是很清楚,对于当时刚刚 接触算法的我,要完全理解还是有一定难度.今天刚好有时间就整理了下思路.重写分析了一下之前的疑惑的地方. 没有透彻的地方便都豁然开朗了.所以迫不及待把我的想法记录下来,和大家分享. 如果你也是和之前的我一样对hanoi tower没能完全消化,或者刚刚接触汉诺塔,那希望我的这种理解方式能给

左神算法书籍《程序员代码面试指南》——1_06用栈来求解汉诺塔问题

[问题] 汉诺塔问题比较经典,这里修改一下游戏规则:现在限制不能从最左侧的塔直接移动到最右侧,也不能从最右侧直接移动到最左侧,而是必须经过中间.求当塔有N层的时候,打印最优移动过程和最优移动总步数.例如,当塔数为两层时,最上层的塔记为1,最下层的塔记为2,则打印:Move 1 from left to mid Move 1 from mid to right Move 2 from left to midMove 1 from right to mid Move 1 from mid to le

C#递归解决汉诺塔问题(Hanoi)

using System;using System.Collections.Generic;using System.Linq;using System.Text; namespace MyExample_Hanoi_{    class Program    {        static void Main(string[] args)        {            HanoiCalculator c = new HanoiCalculator();            Cons

bzoj千题计划109:bzoj1019: [SHOI2008]汉诺塔

http://www.lydsy.com/JudgeOnline/problem.php?id=1019 题目中问步骤数,没说最少 可以大胆猜测移动方案唯一 (真的是唯一但不会证) 设f[i][j] 表示 从i号柱子 上把j个盘子移到 g[i][j] 柱子上的步数 初始化:f[0][1]=1,g[0][1] 根据优先级决定 设三根柱子分别为0,1,2 对于每一个f[x][i], 把前i-1个移走,把第i个移走,把前i-1个移回 令y=g[x][i-1],则k=0+1+2-x-y 我们希望 把i-