Chapter 3 | Stacks and Queues--栈解决汉诺塔问题
来源:互联网 发布:软件专业大专课程 编辑:程序博客网 时间:2024/05/14 21:57
- Only one disk can be moved at a time.
- A disk is slid off the top of one rod onto the next rod.
- A disk can only be placed on top of a larger disk.
Write a program to move the disks from the first rod to the last using Stacks
译文:就是利用栈这一数据结构来解决汉诺塔问题。
汉诺塔是一个经典问题:有三根杆子编号 A,B,C,A 上有N(N>1)穿孔圆盘,盘的尺寸由下到上依次变小,呈塔形,要求按以下规则将盘移动到C:
- 每次只能移动一个盘
- 盘只能从顶部移动到其余杆
- 大盘不能叠在小盘上面
利用栈解决汉诺塔问题。
一、递归
对于汉诺塔这个经典问题,我们先用递归来实现(递归:不必纠结于内部实现,明确其功能即可)
如上图所示,要将 A 杆的 n 个圆盘移动到 C 盘,B 盘则是作为一个中介,假定初始状态为(1~n, 0, 0),那么其最终状态为(0, 0, 1~n)。考虑到递归实现,则必然会有这样一个中间状态(n,1~n-1,0),然后其下一个状态就是(0,1~n-1,n),编号 n 的最大盘位置已经正确,这样等效于(0,1~n-1,0)n位置已经确定可忽略,这个状态和初始状态如出一辙,此时 A 盘成了中介盘,然后就是(1~n-2,n-1,0),接下来便是(1~n-2,0,n-1)已经确定好位置的忽略,这样 n 和 n-1 位置确定,忽略,就变成了初始状态(1~n-2,0,0)。实际状态为(1~n-2,0,n-1~n)。当 A 只有一个盘子,也就是状态为(n,0,0)时(位置确定的忽略)直接移动到 C 盘,当 n = 1 时,为递归的终止条件。
将上面整理一下:移动过程中有三个状态
初始状态:(1~n,0,0)
中间状态:(n,1~n-1,0)
最终状态:(0,0,1~n)
先定义汉诺塔函数原型
void hanoi(int n, char A, char B, char C);函数的功能就是:A 借助 B 移动 n 个盘到 C。(其中 n A B C 均表示函数 hanoi 对应位置参数,而不是某个具体盘)
总体:很明显初始状态(1~n,0,0)达到最终状态(0,0,1~n)就是
hanoi(n, A, B, C); //A 借助 B 移动 n 个盘到 C分步一:先要从初始状态(1~n,0,0)进入中间状态(n,1~n-1,0)就是
hanoi(n-1, A, C, B); //A 借助 C 移动 n-1 个盘到 B分步二:然后从中间状态(n,1~n-1,0)达到最终状态(0,0,1~n),由于此时的中间状态 A 上只有一个盘子,且为未确定位置中的最大盘子,则直接将该盘子移动到 C,然后进入下一轮的递归。所以接下来进入递归的是这样一个情况:
从中间状态(0,1~n-1,0)达到最终状态(0,0,1~n)就是
hanoi(n-1, B, A, C); //B 借助 A 移动 n-1 个盘到 C每一轮后有一个盘子确定好位置,即下一轮只需考虑 n-1 个盘子。
下面贴程序:
这里修改一下函数原型,使其可以返回移动的次数。
using namespace std;void hanoi(int n, char A, char B, char C, int *cnt){if (1 == n){++(*cnt);cout << "直接将编号为" << n << "的盘子从" << A << "移动到" << C << endl;}else{++(*cnt);hanoi(n - 1, A, C, B, cnt);cout << "直接将编号为" << n << "的盘子从" << A << "移动到" << C << endl;hanoi(n - 1, B, A, C, cnt);}}int main(){int n;int cnt = 0;cout << "输入要移动的盘子个数 n = ";cin >> n;hanoi(n, 'A', 'B', 'C', &cnt);cout << "移动的总次数为:" << cnt << endl;return 0;}输入数值的时候,不要手一抖输入 64 或其余相对大的值,不然。。。
二、栈
这里就是真正的解答题目了,递归是一个天然栈,我们可以沿用前面递归的思想,将其通过栈来实现。
前面讨论到汉诺塔的移动要经过以下几个状态
初始状态:(1~n,0,0)
中间状态:(n,1~n-1,0)
次中间状态:(0,1~n-1,n)
最终状态:(0,0,1~n)
上面的第二个中间状态是当 A 只有一个盘子时,就直接将其移动到 C 盘。
这里是将移动过程状态压栈,首先需要定义一个数据结构来保存这些操作的过程状态。
struct oprt{int begin, end;char src, bri, dst;oprt(){}oprt(int _begin, int _end, char _src, char _bri, char _dst):begin(_begin), end(_end), src(_src), bri(_bri), dst(_dst){}};上面的 begin 和 end 表明 src 参数位置盘子的起止位置,当(begin == end)表示 src 参数只有一个盘子,这里指的是第三个参数标明的杆的盘子情况。
后面三个参数位置表示:src 通过 bri 将盘子移动到 dst。(将src,bri,dst 换为参数指定的位置更容易理解)
前面说明有四个状态,那么就有三个过程状态,分别为:
初始状态 --> 中间状态:(1~n,0,0) --> (n,1~n-1,0)
中间状态 --> 次中间状态:(n,1~n-1,0) --> (0,1~n-1,n)
次中间状态 --> 最终状态:(0,1~n-1,n) --> (0,0,1~n)
其中,中间状态 --> 次中间状态,A 杆只有一个盘子,那么直接将该盘子移动到 C 盘即可(同前面递归一样)。
这些过程需要进行压栈出栈处理,压栈的时候不作处理,出栈时进行处理,所以在压栈顺序与实际顺序相反。一开始我们把目的压栈,即(初始状态-->最终状态)压栈,然后将其弹出,并判断其是否为原子操作,也就是说是否只需移动一个盘,如果是则直接移动,否则将其分解为三个状态,然后再行判断,直至操作为原子操作。
void hanoi(int n, char src, char bri, char dst, int *cnt){stack<oprt> Stack;oprt temp;Stack.push(oprt(1, n, src, bri, dst)); //初始状态 ——> 最终状态 while (!Stack.empty()){temp = Stack.top();Stack.pop();if (temp.begin != temp.end) //非原子操作,分解{//次中间状态 ——> 最终状态//将B中1~n-1的盘子通过A移动到CStack.push(oprt(temp.begin, temp.end - 1, bri, src, dst));//中间状态 ——> 次中间状态//将A中编号n的盘子通过B移动到CStack.push(oprt(temp.end, temp.end, src, bri, dst));//初始状态 ——> 中间状态//将A中1~n-1的盘子通过C移动到BStack.push(oprt(temp.begin, temp.end - 1, src, dst, bri));}else //原子操作,直接移动{cout << "Move disk " << temp.begin << " from " << temp.src << " to " << temp.dst << endl;++(*cnt); //移动次数}}}原子操作也叫不可分割的操作,这里是说只移动一个盘子。上面程序需要理解的是压栈保存的是过程状态,不是单一的状态。
- Chapter 3 | Stacks and Queues--栈解决汉诺塔问题
- Chapter 3 | Stacks and Queues
- Chapter 3 | Stacks and Queues--一个数组实现三个栈
- Chapter 3 | Stacks and Queues--两个栈实现队列
- Chapter 3 Stacks and Queues - 3.1
- Chapter 3 Stacks and Queues - 3.2
- Chapter 3 Stacks and Queues - 3.3
- Chapter 3 Stacks and Queues - 3.4
- Chapter 3 Stacks and Queues - 3.5
- Chapter 3 Stacks and Queues - 3.6
- !!!Chapter 3 Lists, Stacks, and Queues
- CareerCup chapter 3 Stacks and Queues
- [cc150]Chapter 3 | Stacks and Queues
- Chapter 3 | Stacks and Queues--一个数组实现三个栈(续)
- Chapter 3 | Stacks and Queues--实现一个函数返回栈中的最小值,时间复杂度为O(1)
- 【CareerCup】Stacks and Queues—Q3.3
- 1-3 Bags,Queues and Stacks
- Chapter3--Stacks and Queues
- iOS教程:使用MKNetworkKit图像高速缓存和加载缩略图
- 5.Redis安全机制
- django+python+操作数据库多表关联-增删改查-many-to-many-many-to-one
- 关于Java的socket服务端项目如何打包成exe文件运行在服务器上。
- 链栈相关
- Chapter 3 | Stacks and Queues--栈解决汉诺塔问题
- 关于char * c="hello"与char c[]="hello"的区别
- chromium相关问题集合--持续更新中
- Android UI倒圆角边框
- 那些年,错过的时光(三)
- B/S 测试
- ORA-27102: out of memory
- 我的随笔文章专栏
- getWriter() getOutputStream() 对比 不能同时使用