汉诺塔(必须经过中间柱子)递归与非递归详解与代码实现
来源:互联网 发布:mac开机开到一半黑屏 编辑:程序博客网 时间:2024/04/29 03:46
首先介绍一下汉诺塔最初始的规则:
有三根相邻的柱子,标号为A,B,C,A柱子从上到下按照金字塔状叠放着n个不同大小的圆盘,现在把所有的盘子一个一个移动到柱子B上,并且每次移动同一根柱子上都不能出现大盘子在小盘子上方。
这是最初始的规则,实现的思路可以分为两个步骤:
(假设圆盘期初都在左边的柱子上,想移动到右边的柱子上)
1.如果只有一个圆盘,直接把左边的圆盘移动到右边。
2.如果有n个圆盘(n>1),先把1—n–1圆盘移动到中间的柱子上,最后一个圆盘进行第一个步骤,之后再把1—n-1圆盘从中间移动到右边。
给出一个博文的链接:打开链接,该博主写得简单易懂,十分推荐。
升级规则:不能将圆盘直接从左边移动到右边,必须经过中间的柱子
递归法:
其实思路与升级前的递归是一样的:
1.当只有一个圆盘时,先从左移到中间,再从中间移动到右边。
2.当有两个圆盘时,先把1(最上面的圆盘)从左边移到中间,再把1移动到右边。把2(1圆盘下面的圆盘)从左边移动到中间,然后把1从右边移动到中间,再把1移动到左边,把2从中间移动到右边,把1从左边移动到中间,再移动到右边。
3.……
总结来说我们要做的有两个步骤:
1、当只要一个圆盘时:
1)如果起点或者终点中有一个为中间柱子,直接把圆盘从起始柱子移动到目标柱子。
2)如果起点和终点都不为中间的柱子,则需要两步,首先把圆盘从起始柱子移动到中间柱子,在从中间柱子移动到目标柱子。
2、当有n(n>1)个圆盘时,需要把1—n-1圆盘从左边移动到右边,再将最底下的圆盘n移动到中间,把1—n-1圆盘从右边移动到左边,把圆盘n从中间移动到右边,1—n-1圆盘从左边移动到右边。
代码:
//hannuota II 递归#include<iostream>#include<vector>#include<string>using namespace std;/*变量含义* n:圆盘数量*left:左柱子*mid:中间柱子*right:右柱子*from:起点柱子*to:目标柱子*/long hannuota(int n,string left,string mid,string right,string from,string to){ if(n == 1) { if(from == mid||to == mid) { cout<<"将"<<n<<"从"<<from<<"移动到"<<to<<endl; return 1; } else { cout<<"将"<<n<<"从"<<from<<"移动到"<<mid<<endl; cout<<"将"<<n<<"从"<<mid<<"移动到"<<to<<endl; return 2; } } else { long num1 = hannuota(n-1,left,mid,right,from,to); cout<<"将"<<n<<"从"<<from<<"移动到"<<mid<<endl; long num2 = hannuota(n-1,left,mid,right,to,from); cout<<"将"<<n<<"从"<<mid<<"移动到"<<to<<endl; long num3 = hannuota(n-1,left,mid,right,from,to); return num1+num2+num3+2; }}int main(){ int n; cout<<"盘数:"; cin>>n; string from = "left"; string depend = "mid"; string to = "right"; if(n>=1) { cout<<"步数"<<hannuota(n,from,depend,to,from,to)<<endl; } return 0;}
测试:
当然,上述的代码只能实现将n个圆盘从左边全部移到右边,或者右边移到左边,如果起点或者终点为中间圆盘,即会出错。但是实现全部只需要再加一种情况就好了。
(全面考虑)如果起点或者终点中有一个为中间柱子:
只剩一个圆盘时,直接移动到目标柱子,上面的代码已经包含,
不止一个圆盘时,需要把1—n-1圆盘移动到过渡柱子上(既不是起始柱子,也不是终点柱子),然后把圆盘n从起点移到终点,在把1—n-1从过渡移到终点。
更改 hannuota()函数
long hannuota(int n,string left,string mid,string right,string from,string to){ if(n == 1) { if(from == mid||to == mid) { cout<<"将"<<n<<"从"<<from<<"移动到"<<to<<endl; return 1; } else { cout<<"将"<<n<<"从"<<from<<"移动到"<<mid<<endl; cout<<"将"<<n<<"从"<<mid<<"移动到"<<to<<endl; return 2; } } else { if(from == mid||to == mid) { string depend = (from == left||to == left)?right:left; long num1 = hannuota(n-1,left,mid,right,from,depend); cout<<"将"<<n<<"从"<<from<<"移动到"<<to<<endl; long num2 = hannuota(n-1,left,mid,right,depend,to); return num1+num2+1; } else { long num1 = hannuota(n-1,left,mid,right,from,to); cout<<"将"<<n<<"从"<<from<<"移动到"<<mid<<endl; long num2 = hannuota(n-1,left,mid,right,to,from); cout<<"将"<<n<<"从"<<mid<<"移动到"<<to<<endl; long num3 = hannuota(n-1,left,mid,right,from,to); return num1+num2+num3+2; } }}
至此,递归方法已经说明
非递归:
借助栈实现,构建3个栈,分别代表3个柱子。整个汉诺塔的步骤其实只有4步,从左移到中间,从中间移到左边,从中间移到右边,从右边移到中间。而且这四步中满足两个条件:
1、相邻不可逆:意思就是我执行了“从中间移到左边”,下一步就不行执行“从左边移到中间”,不然之前的步骤就没有意义了,求出来的也不是最短的步数。
2、 小压大原则:压入的圆盘必须比柱子上最上面的圆盘小,否则不行。
而且每次选择时,可以根据这两个条件,排除4步中其他3步,只有1个满足两个条件,因此就选择那种方法移动。证明:
假设上一步是“从左到中间”,则根据相邻不可逆排除“从中间到左“,而且根据小压大,“从中间到 右”和“从右到中间”只有一个符合,因此下一步是可以根据这两个条件来唯一确定的。
代码实现:
//hanbuota II 非递归#include<iostream>#include<vector>#include<string>#include<stack>using namespace std;enum Action{ NO,LTOM,MTOL,RTOM,MTOR};int fstackTotstack(Action &a,Action preAction,Action nowAction,stack<int>&fstack,stack<int >&tstack,string from,string to){ if(preAction != a&&fstack.size()!=0&&(tstack.size()==0||fstack.top()<tstack.top())) { tstack.push(fstack.top()); cout<<"将"<<fstack.top()<<"从"<<from<<"移动到"<<to<<endl; fstack.pop(); a = nowAction; return 1; } return 0;}int hannuota(int n,string left,string mid,string right){ stack<int>fstack; stack<int>mstack; stack<int>tstack; for(int i = n;i>=1;i--) fstack.push(i); int step = 0; Action a = NO; while(tstack.size()!=n) { step += fstackTotstack(a,MTOL,LTOM,fstack,mstack,left,mid); step += fstackTotstack(a,LTOM,MTOL,mstack,fstack,mid,left); step += fstackTotstack(a,RTOM,MTOR,mstack,tstack,mid,right); step += fstackTotstack(a,MTOR,RTOM,tstack,mstack,right,mid); } return step;}int main(){ int n; string left = "left"; string mid = "mid"; string right = "right"; while(cin>>n) { if(n>=1) cout<<"步数"<<hannuota(n,left,mid,right)<<endl; } return 0;}
测试:
注意:当起始圆盘不在左边柱子上时,需要更改四个fstackTotstack()函数的顺序。
- 汉诺塔(必须经过中间柱子)递归与非递归详解与代码实现
- 汉诺塔 递归与非递归实现 (1)
- 汉诺塔 递归与非递归实现 (2)
- 快速排序递归与非递归代码实现与思考
- 汉诺塔递归与非递归
- Fibonacci数列的递归与非递归实现算法详解
- 递归与非递归
- 递归与非递归
- 递归与非递归
- 递归与非递归
- 递归与非递归
- 递归与非递归
- 汉诺塔的递归实现与非递归实现
- 汉诺塔(递归实现与非递归实现)
- 矩阵相乘--递归与非递归实现
- 二分查找递归与非递归实现
- 折半查找递归与非递归实现
- 最大公约数(递归与非递归实现),最小公倍数
- leetcode 237. Delete Node in a Linked List
- 页式管理--物理地址计算问题小结
- 如何让Vim Gui启动时默认自动最大化
- 数据通路的习题研究
- javaWeb之Servlet装载情况
- 汉诺塔(必须经过中间柱子)递归与非递归详解与代码实现
- 剑指offer_数组中出现次数超过一半的数字
- Ubuntu下搭建我的世界服务器
- 1613-3-傅溥衍 总结《2016年10月4日》【连续第四天总结】
- Shell编程基础
- 指针 练习
- redis 源码阅读笔记
- Centreon+Nagios实战第六篇——进行Centreon web界面设置
- Android多分辨率适配框架(1)— 核心基础