用栈模拟汉诺塔问题

描述

在经典的汉诺塔问题中,有 3 个塔和 N 个可用来堆砌成塔的不同大小的盘子。要求盘子必须按照从小到大的顺序从上往下堆 (如:任意一个盘子,其必须堆在比它大的盘子上面)。同时,你必须满足以下限制条件:

  1. 每次只能移动一个盘子。
  2. 每个盘子从堆的顶部被移动后,只能置放于下一个堆中。
  3. 每个盘子只能放在比它大的盘子上面。

请写一段程序,实现将第一个堆的盘子移动到最后一个堆中。

样例

输入

3

输出

towers[0]: []

towers[1]: []

towers[2]: [2,1,0]

思路

因为自己以前没玩过,也没有纸和笔,另外一直在想知道每个步骤到底应该怎么做,应该从哪个移到哪个,所以到最后也没有想出一个可行的办法,最后迫不得已去搜了一下,结果才发现自己一直在钻牛角尖:

当只有一个盘子的时候,只需要从将A塔上的一个盘子移到C塔上。

当A塔上有两个盘子是,先将A塔上的1号盘子(编号从上到下)移动到B塔上,再将A塔上的2号盘子移动的C塔上,最后将B塔上的小盘子移动到C塔上。

当A塔上有3个盘子时,先将A塔上编号1至2的盘子(共2个)移动到B塔上(需借助C塔),然后将A塔上的3号最大的盘子移动到C塔,最后将B塔上的两个盘子借助A塔移动到C塔上。

当A塔上有n个盘子是,先将A塔上编号1至n-1的盘子(共n-1个)移动到B塔上(借助C塔),然后将A塔上最大的n号盘子移动到C塔上,最后将B塔上的n-1个盘子借助A塔移动到C塔上。

综上所述,除了只有一个盘子时不需要借助其他塔外,其余情况均一样(只是事件的复杂程度不一样)。

以上来自于

看过上面这段话,应该都能写出来代码了,不过为了照顾以后的自己,我还是把我自己的思路写下来吧:

三个塔:A,B,C。需要把盘子从A中移动到C中。

三个塔在每次移动时每个都有一个身份,分别为“源塔”(最开始盘子最多的塔)、“目标塔”(最后要把所有的盘子堆起来的塔)、“辅助塔”(与前面两个相对而言),每个塔与身份并不固定。

当只有一个盘子的时候:直接把A(源塔)中的盘子放到C(目标塔)中。结束!

当有两个盘子的时候【目标:把A(源塔)的两个盘子放到C(目标塔)中】:只用三步:

  1. 把A(源塔)中最小的盘子放到B(辅助塔)中
  2. 再将A(源塔)中的第二个盘子放到C(目标塔)中
  3. 最后再把B(辅助塔)中的最小的盘子放到C(目标塔)中

结束!

当有三个盘子的时候:用两个盘子时的思维,只用三步:

  1. 我们只用将A(源塔)中的前两个盘子放到B(辅助塔)中
  2. 再将A(源塔)中第三个盘子放到C(目标塔)中
  3. 最后再把B(辅助塔)中的两个盘子放到C(目标塔)中

结束——就怪了。因为还没考虑怎么将A的两个盘子放到B中,所以这个时候的目标就变了,变成了把A中的前两个盘子放到B中,这时候就很明显了,只要把塔的身份变换一下,就和两个盘子时的操作一模一样了。之后第二步也很简单。到了第三步,这个时候,A没有盘子,B有两个盘子,C有一个最大的盘子,这个时候的目标是把B中的两个盘子放到C中。是的,和两个盘子时的目标又是类似,所以替换一下身份再来一次,问题就真的解决了!

所以,当有N个盘子的时候,我们也只要三步:

  1. 把A的N-1个盘子放到B中
  2. 把A所剩下唯一的也是最大的盘子放到C中
  3. 把B中所有的盘子都放到C中

对于第一步,我们又可以转化为这三步:

此时的目标是把盘子从A放到B中

  1. 把A的N-2个盘子放到C中
  2. 把A最大的盘子(对于那N-1个盘子来说)放到B中
  3. 把C中所有的盘子都放到B中

对于这个的第一步,我们又可以……

由此,我们就可以写出代码:

代码

其中的moveTopTomoveDisks方法为需要填补的内容

public class Tower {
private Stack<Integer> disks;
// create three towers (i from 0 to 2)
public Tower(int i) {
disks = new Stack<Integer>();
} // Add a disk into this tower
public void add(int d) {
if (!disks.isEmpty() && disks.peek() <= d) {
System.out.println("Error placing disk " + d);
} else {
disks.push(d);
}
} // @param t a tower
// Move the top disk of this tower to the top of t.
public void moveTopTo(Tower t) {
// Write your code here
if (t.disks.isEmpty() || (!disks.isEmpty() && t.disks.peek() >= disks.peek())) {
t.disks.push(disks.pop());
}
} // @param n an integer
// @param destination a tower
// @param buffer a tower
// Move n Disks from this tower to destination by buffer tower
public void moveDisks(int n, Tower destination, Tower buffer) {
// Write your code here
if (n <= 0) {
return;
} else if (n == 1) {
moveTopTo(destination);
} else {
moveDisks(n - 1, buffer, destination); //将源塔前几个盘子都放到辅助塔中
moveDisks(1, destination, buffer); //此时源塔只剩下一个最大的盘子,将它放到目标塔中
buffer.moveDisks(n - 1, destination, this); //最后再将辅助塔的全部盘子(因为此时目标塔上已经有了一个盘子,故辅助塔的盘子数为n-1)都放到目标塔上
}
} public Stack<Integer> getDisks() {
return disks;
}
}
/**
* Your Tower object will be instantiated and called as such:
* Tower[] towers = new Tower[3];
* for (int i = 0; i < 3; i++) towers[i] = new Tower(i);
* for (int i = n - 1; i >= 0; i--) towers[0].add(i);
* towers[0].moveDisks(n, towers[2], towers[1]);
* print towers[0], towers[1], towers[2]
*/

写在最后

这次可能写的思路有点啰嗦,不过自己感觉理了一遍以后思路更加清晰了,这个方法是用的递归,我也看了非递归的方法,不过感觉非递归的思想感觉比这个更难理解,所以就放弃了,先把思路复制到这里,等以后有空的话再试着理一理吧。

其实算法非常简单,当盘子的个数为n时,移动的次数应等于2^n – 1(有兴趣的可以自己证明试试看)。后来一位美国学者发现一种出人意料的简单方法,只要轮流进行两步操作就可以了。首先把三根柱子按顺序排成品字型,把所有的圆盘按从大到小的顺序放在柱子A上,根据圆盘的数量确定柱子的排放顺序:若n为偶数,按顺时针方向依次摆放 A B C;若n为奇数,按顺时针方向依次摆放 A C B。

⑴按顺时针方向把圆盘1从现在的柱子移动到下一根柱子,即当n为偶数时,若圆盘1在柱子A,则把它移动到B;若圆盘1在柱子B,则把它移动到C;若圆盘1在柱子C,则把它移动到A。

⑵接着,把另外两根柱子上可以移动的圆盘移动到新的柱子上。即把非空柱子上的圆盘移动到空柱子上,当两根柱子都非空时,移动较小的圆盘。这一步没有明确规定移动哪个圆盘,你可能以为会有多种可能性,其实不然,可实施的行动是唯一的。

⑶反复进行⑴⑵操作,最后就能按规定完成汉诺塔的移动。

所以结果非常简单,就是按照移动规则向一个方向移动金片:

如3阶汉诺塔的移动:A→C,A→B,C→B,A→C,B→A,B→C,A→C

来自汉诺塔_百度百科

【LintCode·容易】用栈模拟汉诺塔问题的更多相关文章

  1. Python使用函数模拟“汉诺塔”过程

    运行效果: 源代码: 1 # -*- coding:utf-8 -*- 2 ##汉诺塔游戏开始 3 _times=0 #用于统计移动次数 4 def hannuota(nlist,mfrom,mpas ...

  2. [javascript]模拟汉诺塔

    看了博文自己动手写了代码. 这能值几个钱? 请写代码完成汉诺塔的算法:void Hanoi(int maxLevel); 比如2层汉诺塔,需要打印(Console.WriteLine)出如下文本: A ...

  3. 算法笔记_013:汉诺塔问题(Java递归法和非递归法)

    目录 1 问题描述 2 解决方案  2.1 递归法 2.2 非递归法 1 问题描述 Simulate the movement of the Towers of Hanoi Puzzle; Bonus ...

  4. Java实现汉诺塔问题

    1 问题描述 Simulate the movement of the Towers of Hanoi Puzzle; Bonus is possible for using animation. e ...

  5. js模拟栈---汉诺塔

    var Stack = (function(){ var items = new WeakMap(); //先入后出,后入先出 class Stack{ constructor(){ items.se ...

  6. LintCode 汉诺塔

    题目链接:https://www.lintcode.com/problem/tower-of-hanoi/description 题目大意 经典递归问题. 分析 由于是经典问题了,这里不讨论用递归实现 ...

  7. 数据结构--汉诺塔--借助栈实现非递归---Java

    /*汉诺塔非递归实现--利用栈 * 1.创建一个栈,栈中每个元素包含的信息:盘子编号,3个塔座的变量 * 2.先进栈,在利用循环判断是否栈空, * 3.非空情况下,出栈,检查是否只有一个盘子--直接移 ...

  8. 汉诺塔问题II(模拟)

    汉诺塔问题II Time Limit: 1 Sec  Memory Limit: 64 MB Submit: 1556  Solved: 720 Description 汉诺塔(又称河内塔)问题是源于 ...

  9. 汉诺塔VII(递推,模拟)

    汉诺塔VII Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) Total Submis ...

随机推荐

  1. Java基础(00)

    Java发展史 Java之父:詹姆斯.高斯林(James Gosling). SUN(Stanford University Network 斯坦福大学网络公司)产物. 1995年5月23日,java ...

  2. Mybatis Generator生成Mybatis Dao接口层*Mapper.xml以及对应实体类

    [前言] 使用Mybatis-Generator自动生成Dao.Model.Mapping相关文件,Mybatis-Generator的作用就是充当了一个代码生成器的角色,使用代码生成器不仅可以简化我 ...

  3. AsyncTask学习

    在学习Android的时候,开始用到比较多的异步处理的类大概就是AsyncTask,但是我们很多时候只知道调用,却不知道思考一些东西. 本文就简单的总结和分析了一些AsyncTask的知识. 一.As ...

  4. enum(枚举类型)

    可以使用枚举类型声明代表整数常量的符号名称. 通过enum,创建一个新类型,并指定它可以拥有的值.(就像平常用一个整形变量,我们指定它等于0的时候代表什么,1呢,2呢...而通过枚举,就增加了程序的可 ...

  5. CodePath Android CliffNotes 之 Effective Java for Android 翻译

    概述: 这篇文章的目的是作为这篇博文的开源版本,而netcyrax是该指南的原始文章的唯一作者. 请在下面添加您自己的在Android中Java最佳实践.技巧和巧妙! 建造者模式 当你拥有一个需要超过 ...

  6. Lua 5.3 协程简单示例

    Lua 5.3 协程简单示例 来源 http://blog.csdn.net/vermilliontear/article/details/50547852 生产者->过滤器->消费者 模 ...

  7. Lucky Coins Sequence

    Lucky Coins Sequence Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others ...

  8. windows 上rsync客户端使用方法

    1.1 获取 windows上实现rsync的软件(cwRsync) cwRsync是Windows 客户端GUI的一个包含Rsync的包装.您可以使用cwRsync快速远程文件备份和同步. 1.1. ...

  9. 解析 C# 7中的元组类型(ValueTuple)

    System.Tuple 类型是在.NET 4.0中引入的,但是有两个明显的缺点: (1) Tuple 类型是引用类型. (2) 没有构造函数支持. 为了解决这些问题,C# 7 引入了新的语言功能以及 ...

  10. 0_Simple__cudaOpenMP

    在OpenMP的多线程程序中,各线程分别调用CUDA进行计算.OpenMP的简单示例. ▶ 源代码: #include <omp.h> #include <stdio.h> # ...