堆栈应用(二):汉诺塔

1、问题描述

  汉诺塔( Towers of Hanoi)问题来自一个古老的传说:在世界刚被创建的时候有一座钻石宝塔(塔1 ),其上有6 4个金碟(如图 5 - 4所示)。所有碟子按从大到小的次序从塔底堆放至塔顶。紧挨着这座塔有另外两个钻石宝塔(塔 2和塔3)。从世界创始之日起,婆罗门的牧师们就一直在试图把塔 1 上的碟子移动到塔 2上去,其间借助于塔 3的帮助。由于碟子非常重,因此,每次只能移动一个碟子。另外,任何时候都不能把一个碟子放在比它小的碟子上面。按照这个传说,当牧师们完成他们的任务之后,世界末日也就到了。在汉诺塔问题中,已知 n个碟子和 3 座塔。初始时所有的碟子按从大到小次序从塔 1 的底部堆放至顶部,我们需要把碟子都移动到塔 2,每次移动一个碟子,而且任何时候都不能把大碟子放到小碟子的上面。在继续往下阅读之前,可以先尝试对 n= 2 , 3和4来解决这个问题。
   一个非常优雅的解决办法是使用递归。为了把最大的碟子移动到塔 2,必须把其余n- 1 个碟子移动到塔 3,然后把最大的碟子移动到塔 2。接下来是把塔 3上的 n- 1 个碟子移动到塔 2,为此可以利用塔 2和塔1 。可以完全忽视塔 2上已经有一个碟子的事实,因为这个碟子比塔 3上将要移过来的任一个碟子都大,因此,可以在它上面堆放任何碟子。事实上递归本身就是用堆栈实现的。

  

盘子移动次数为:

推导可得moves(n)=2n-1,可以证明这实际上是最少移动次数

  假定希望给出每次移动之后三座塔的状态(即塔上的碟子及其次序),那么必须在内存中保留塔的状态,并在每次移动碟子之后,对塔的状态进行修改。这样每移动一个碟子时,就可以在一个输出设备(如计算机屏幕、打印机等)上输出塔的信息。由于从每个塔上移走碟子时是按照 L I F O的方式进行的,因此可以把每个塔表示成一个堆栈。三座塔在任何时候都总共拥有 个碟子,因此,如果使用链表形式的堆栈,只需申请 n个元素所需要的空间。如果使用的是基于公式化描述的堆栈,塔 1和塔2的容量都必须是 n,而塔3的容量必须为 n- 1 ,因而所需要的空间总数为 3n- 1 。前面的分析已经指出,汉诺塔问题的复杂性是以 为指数的函数,因此在可以接受的时间范围内,只能解决 值比较小(如 n≤ 3 0)的汉诺塔问题。对于这些较小的 值,基于公式描述和基于链表描述的堆栈在空间需求上的差别相当小,因此可以随意使用。
  本文使用基于链表描述的堆栈实现。

2、代码实现

汉诺塔程序(其中LinkedStack.h实现见堆栈的链表方式实现):

 1 #ifndef HANOICLASS_H
 2 #define HANOICLASS_H
 3 #include <iostream>
 4 #include "LinkedStack.h"
 5
 6 using std::cout;
 7 using std::endl;
 8
 9 class Hanoiclass
10 {
11     friend void TowersOfHanoi(int);
12 public:
13     void TowersOfHanoi(int n, int x, int y, int z);//递归解决汉诺塔
14 private:
15     LinkedStack<int> *S[4];
16     void ShowState();//输出3个塔的状态
17 };
18
19 void Hanoiclass::TowersOfHanoi(int n, int x, int y, int z)
20 {
21     if (n > 0)
22     {
23         TowersOfHanoi(n - 1, x, z, y);
24         cout << "Move top disk from tower " << x << "to top of tower " << y << std::endl;
25         int temp;
26         S[x]->Delete(temp);//从x塔顶移出盘子
27         S[y]->Add(temp);//盘子移入y塔顶
28         ShowState();
29         TowersOfHanoi(n - 1, z, y, x);
30     }
31 }
32
33 void TowersOfHanoi(int n)
34 {
35     Hanoiclass X;
36     for (int i = 1; i < 4;++i)
37     {
38         X.S[i] = new LinkedStack<int>;
39     }
40
41     for (int d = n; d>0;--d)
42     {
43         X.S[1]->Add(d);
44     }
45     X.ShowState();
46     X.TowersOfHanoi(n, 1, 2, 3);
47
48 }
49
50 void Hanoiclass::ShowState()
51 {
52     for (int i = 1; i < 4;++i)
53     {
54         cout << "塔"<<i<<": ";
55         if (!S[i]->IsEmpty())
56         {
57             cout << *S[i];
58         }
59         cout << endl;
60     }
61
62 }
63 #endif

运行:

 1 // Hanoi.cpp : 定义控制台应用程序的入口点。
 2 //
 3
 4 #include "stdafx.h"
 5 #include<iostream>
 6 #include "Hanoiclass.h"
 7 using std::cout;
 8 using std::cin;
 9
10 int _tmain(int argc, _TCHAR* argv[])
11 {
12     TowersOfHanoi(3);
13     system("pause");
14     return 0;
15 }

输出:

时间: 2024-12-19 21:37:48

堆栈应用(二):汉诺塔的相关文章

算法初级面试题08——递归和动态规划的精髓、阶乘、汉诺塔、子序列和全排列、母牛问题、逆序栈、最小的路径和、数组累加成指定整数、背包问题

第八课主要介绍递归和动态规划 介绍递归和动态规划 暴力递归: 1,把问题转化为规模缩小了的同类问题的子问题 2,有明确的不需要继续进行递归的条件(base case) 3,有当得到了子问题的结果之后的决策过程 4,不记录每一个子问题的解 动态规划 1,从暴力递归中来 2,将每一个子问题的解记录下来,避免重复计算 3,把暴力递归的过程,抽象成了状态表达 4,并且存在化简状态表达,使其更加简洁的可能 图灵引入的是:我不知道怎么算,但是我知道怎么试.知道怎么暴力破解出来. 要学会,练习懂得怎么尝试.

【二色汉诺塔 】

/* 二色汉诺塔 */ #include <stdio.h> void hanoi(int disks, char sources, char temp, char target) { if(disks == 1) { printf("move disk from %c to %c \n", sources, target); printf("move disk from %c to %c \n", sources, target); } else {

nyoj汉诺塔(二)

汉诺塔(二)题目链接 汉诺塔问题的经典结论:把i个盘子从一个柱子整体移到另一个柱子最少需要步数是 2的i次方减一.那我们这个给定一个初始局面,求他到目标局面(全部移到第三个柱子上)需要的最少步数.怎么办呢!! 分析: 1.总的来说一定是先把最大的盘子移到第三个柱子上, 然后再把第二大的移到柱子3上, 然后再把第三大的盘子移到柱子3上---直到把最小的盘子(1号盘子)移到柱子3上,才算结束. 2.现在设想一下,在移动第k个盘子动作前,柱子上的整体情况, 假设盘子k在柱子1上, 要移到柱子3上, 由

汉诺塔(二)(思维)

汉诺塔(二) 时间限制:3000 ms  |  内存限制:65535 KB 难度:5 描述 汉诺塔的规则这里就不再多说了,详见题目:汉诺塔(一) 现在假设规定要把所有的金片移动到第三个针上,给你任意一种处于合法状态的汉诺塔,你能计算出从当前状态移动到目标状态所需要的最少步数吗? 输入 第一行输入一个整数N,表示测试数据的组数(0<N<20)每组测试数据的第一行是一个整数m表示汉诺塔的层数(0<m<32),随后的一行有m个整数Ai,表示第i小的金片所在的针的编号.(三根针的编号分别为

nyoj89 汉诺塔(二)

题目网址 :http://acm.nyist.net/JudgeOnline/problem.php?pid=89 汉诺塔问题的经典结论: 把i个盘子从一个柱子整体移到另一个柱子最少需要步数是 2的i次方减一.那我们这个给定一个初始局面,求他到目标局面(全部移到第三个柱子上)需要的最少步数.怎么办呢!! 分析: 1.总的来说一定是先把最大的盘子移到第三个柱子上, 然后再把第二大的移到柱子3上, 然后再把第三大的盘子移到柱子3上.........直到把最小的盘子(1号盘子)移到柱子3上,才算结束.

从汉诺塔问题来看“递归”本质

汉诺塔问题 大二上数据结构课,老师在讲解"栈与递归的实现"时,引入了汉诺塔的问题,使用递归来解决n个盘在(x,y,z)轴上移动. 例如下面的动图(图片出自于汉诺塔算法详解之C++): 三个盘的情况: 四个盘的情况: 如果是5个.6个.7个....,该如何移动呢? 于是,老师给了一段经典的递归代码: void hanoi(int n,char x,char y,char z){ if(n == 1) move(x,1,z); else{ hanoi(n-1,x,z,y); move(x,

简述java递归与非递归算法,0-100求和,斐波那契数列,八皇后,汉诺塔问题

一:什么是递归算法? 递归算法就是直接或者间接的调用自己的方法,在达到一个条件的时候停止调用(递归出口),所以一定要找准好条件,让递归停止,否则就会是无限进行下去 二:递归程序设计的关键 1:找出调用中所需要的参数 2:返回的结果 3:递归调用结束的条件 三:递归程序注意 1:要有方法中自己调用自己 2:要有分支结构 3:要有结束的条件 四:简单叙述递归函数的优缺点 优点: 1:简洁清晰,实现容易,可读性好 2:在遍历的算法中,递归比循环更为简单 缺点: 1:效率低,使用递归函数是有空间和时间的

bzoj1019: [SHOI2008]汉诺塔(dp)

1019: [SHOI2008]汉诺塔 Time Limit: 1 Sec  Memory Limit: 162 MBSubmit: 1739  Solved: 1062[Submit][Status][Discuss] Description 汉诺塔由三根柱子(分别用A B C表示)和n个大小互不相同的空心盘子组成.一开始n个盘子都摞在柱子A上,大的在下面,小的在上面,形成了一个塔状的锥形体. 对汉诺塔的一次合法的操作是指:从一根柱子的最上层拿一个盘子放到另一根柱子的最上层,同时要保证被移动的

汉诺塔的递归算法与解析

从左到右 A  B  C 柱 大盘子在下, 小盘子在上, 借助B柱将所有盘子从A柱移动到C柱, 期间只有一个原则: 大盘子只能在小盘子的下面. 如果有3个盘子, 大中小号, 越小的越在上面, 从上面给盘子按顺序编号 1(小),2(中),3(大), 后面的原理解析引用这里的编号. 小时候玩过这个游戏, 基本上玩到第7个,第8个就很没有耐心玩了,并且操作的动作都几乎相同觉得无聊.  后来学习编程, 认识到递归, 用递归解决汉诺塔的算法也是我除了简单的排序算法后学习到的第一种算法. 至于递归,简单来说