算法2:Hanoi塔
汉诺(Hanoi)塔
一、背景介绍
在印度,有这么一个古老的传说:在世界中心贝拿勒斯(在印度北部)的圣庙里,一块黄铜板上插着三根宝石针。印度教的主神梵天在创造世界的时候,在其中一根针上从下到上地穿好了由大到小的64片金片,这就是所谓的汉诺塔。不论白天黑夜,总有一个僧侣在按照下面的法则移动这些金片,一次只移动一片,不管在哪根针上,小片必在大片上面。当所有的金片都从梵天穿好的那根针上移到另外一概针上时,世界就将在一声霹雳中消灭,梵塔、庙宇和众生都将同归于尽。
二、数学定义
从左到右有三根柱子A、B、C,其中A柱子上面有从小叠到大的n个圆盘,现要求将A柱子上的圆盘移到C柱子上去,期间只有一个原则:一次只能移到一个盘子且大盘子不能在小盘子上面,求移动的步骤和移动的次数。
我们记移动总次数为sum。
不难发现,
当n==1时,sum=1,
第一次:A→C;
当n==2时,sum=3,
第一次:A→B,
第二次:A→C,
第三次:B→C;
当n==3时,sum=7;
第一次: A→C,
第二次: A→B,
第三次: C→B,
第四次 :A→C,
第五次 :B→A,
第六次 :B→C,
第七次: A→C;
因此,
不难发现规律:
1个圆盘的次数 2的1次方减1
2个圆盘的次数 2的2次方减1
3个圆盘的次数 2的3次方减1
……
n个圆盘的次数 2的n次方减1
故:移动次数为:2^n - 1。
三、递归算法
void hanoi(int n, int a, int b, int c) {
if (n > 0) {
hanoi(n - 1, a, c, b);
move(a, b);
hanoi(n - 1, c, b, a);
}
}
其中,hanoi(n, a, b, c)表示将塔座A上自下而上,由大到小叠放在一起的n个圆盘依移动规则移至塔座B上并按照同样的顺序叠放。在移动过程中,以塔座C作为辅助塔座。move(a, b)表示将塔座A上编号为n的圆盘移到塔座B上。
实现这个算法可以简单分为三个步骤:
(1) 把n-1个盘子由A 移到 B;
(2) 把第n个盘子由 A移到 C;
(3) 把n-1个盘子由B 移到 C;
从这里入手,在加上上面数学问题解法的分析,我们不难发现,移到的步数必定为奇数步:
(1)中间的一步是把最大的一个盘子由A移到C上去;
(2)中间一步之上可以看成把A上n-1个盘子通过借助辅助塔(C塔)移到了B上,
(3)中间一步之下可以看成把B上n-1个盘子通过借助辅助塔(A塔)移到了C上;
四、完整代码
#include<iostream>
#include<math.h>
using namespace std; void hanoi(int n, char A, char B, char C) {
if (n == 1)
cout << "将圆盘" << n << "从" << A << "移动到" << C << endl;
else {
hanoi(n - 1, A, C, B);
cout<< "将圆盘" << n << "从" << A << "移动到" << C << endl;
hanoi(n - 1, B, A, C);
}
} int main() {
int n;
cout << "请输入A上圆盘的个数n:";
cin >> n;
cout << endl;
hanoi(n, 'A', 'B', 'C');
cout << endl;
int sum = pow(2, n) - 1;
printf("移动的总次数sum为:%d",sum);
return 0;
}
五、非递归(拓展)
该方法设计了一个数据结构 struct act {int flag,num; char x, y, z;} S[2000]; 存储当前操作信息,其中flag==0表示直接移动num号圆盘,否则需要进一步分解;num表示当前操作移动盘子的编号,x,y,z表示当前操作对应的3个塔柱,分别是出发柱,中点柱和目标柱。
void Hanoi_1(int n, char a, char b, char c)//非递归算法1
{
struct act { int flag, num; char x, y, z; } S[2000]; //存储当前操作信息,flag==0表示直接移动num号圆盘,否则需要进一步分解
int top, m;
char ta, tb, tc; S[0].flag = 1;//初值入栈
S[0].num = n;
S[0].x = a;
S[0].y = b;
S[0].z = c;
top = 0;
int count = 0;
while (top >= 0)
{
if (S[top].flag == 0 || S[top].num == 1)//直接将num号圆盘从x移动到z
{
printf("%d: %d %c -> %c ", ++count, S[top].num, S[top].x, S[top].z);
--top;
}
else
{ //提取栈顶信息
m = S[top].num;
ta = S[top].x;
tb = S[top].y;
tc = S[top].z;
//将 Hanoi(n-1, b, a, c); 操作入栈 ,覆盖原栈顶信息
S[top].num = m - 1;
S[top].x = tb;
S[top].y = ta;
S[top++].z = tc;
//将 将第m个盘从a柱移动到c柱 操作入栈
S[top].flag = 0;
S[top].num = m;
S[top].x = ta;
S[top++].z = tc;
//将 Hanoi(n-1, a, c, b); 操作入栈
S[top].flag = 1;
S[top].num = m - 1;
S[top].x = ta;
S[top].y = tc;
S[top].z = tb;
}
}
}
以上非递归算法利用栈模拟递归过程的基本方法。对于有n个盘子的汉诺塔问题,需要操作的步骤为2^n – 1,如果每一个步骤看成一个节点,则刚好构成一棵满二叉树,树高h与盘子数量的关系为h==n。结点所在的层数与对应盘子的编号关系为level==n+1-level,即盘子1在第n层,盘子n在第1层;若某个结点的操作为“盘子n从A->C”,则其左孩子操作为“盘子n-1从A->B”,右孩子操作为“盘子n-1从B->C”;中序遍历满二叉树,结点的编号恰好对应移动的次序。
因此我们可以构造一棵满二叉树,然后中序遍历该二叉树。
void Hanoi_3(int n, char a, char b, char c)
{
struct act { int num; char x, y, z; } BT[132000];
//存储每一步操作的满二叉树
int S[MAX] = { 0 };
int i, top, count = 0; BT[1].num = n;//为根结点赋值
BT[1].x = a;
BT[1].y = b;
BT[1].z = c;
n = pow(2, n - 1);
for (i = 1; i < n; i++)//为每个节点的左右孩子赋值
{
BT[i + i].num = BT[i + i + 1].num = BT[i].num - 1;
BT[i + i].x = BT[i + i + 1].y = BT[i].x;
BT[i + i].z = BT[i + i + 1].x = BT[i].y;
BT[i + i].y = BT[i + i + 1].z = BT[i].z;
} //中序遍历满二叉树
n += n;
i = 1;
top = -1;
while (i < n || top >= 0)
{
if (i < n)//入栈,并搜索左孩子
{
S[++top] = i;
i += i;
}
else//输出并退栈,搜索右孩子
{
i = S[top--];
printf("%d: %d %c -> %c ", ++count, BT[i].num, BT[i].x, BT[i].z);
i += i + 1;
}
}
}
鸣谢:http://blog.csdn.net/qiaoruozhuo
算法2:Hanoi塔的更多相关文章
- 算法训练 Hanoi问题
算法训练 Hanoi问题 时间限制:1.0s 内存限制:512.0MB 问题描述 如果将课本上的Hanoi塔问题稍做修改:仍然是给定N只盘子,3根柱子,但是允许每次最多移动相邻的 ...
- 栈与递归的实现(Hanoi塔问题等等)
函数中有直接或间接地调用自身函数的语句,这样的函数称为递归函数.递归函数用 得好,可简化编程工作.但函数自己调用自己,有可能造成死循环.为了避免死循环,要 做到两点: (1) 降阶.递归函数虽然调用自 ...
- 经典递归算法研究:hanoi塔的理解与实现
关于hanoi塔的原理以及概念,请Google,访问不了去百度. 主要设计到C中程序设计中递归的实现: 主代码实现如下: void hanoi(int src, int dest, int tmp, ...
- (转)Hanoi塔问题分析
转自:http://shmilyaw-hotmail-com.iteye.com/blog/2077098 简介 关于Hanoi塔问题的分析,在网上的文章都写烂了.之所以打算写这篇文章,更多的是针对这 ...
- Hanoi塔问题——递归
/////////////Hanoi塔问题///////#include<iostream>using namespace std;void hanoi(int i,char A,char ...
- Java实现 蓝桥杯VIP 算法训练 Hanoi问题
问题描述 如果将课本上的Hanoi塔问题稍做修改:仍然是给定N只盘子,3根柱子,但是允许每次最多移动相邻的M只盘子(当然移动盘子的数目也可以小于M),最少需要多少次? 例如N=5,M=2时,可以分别将 ...
- (算法)Hanoi Problem汉诺塔问题
Problem: There are three poles and N disks where each disk is heaver than the next disk. In the init ...
- Hanoi塔问题
说明:河内之塔(Towers of Hanoi)是法国人M.Claus(Lucas)于1883年从泰国带至法国的,河内为越战时北越的首都,即现在的胡志明市:1883年法国数学家 Edouard Luc ...
- 【题解】Hanoi塔问题
题目描述 有三根柱A,B,C.在柱A上有N块盘片,所有盘片都是大的在下面,小片能放在大片上面.并依次编好序号,现要将A上的N块片移到C柱上,每次只能移动一片,而且在同一根柱子上必须保持上面的盘片比下面 ...
- python算法-汉诺塔问题
汉诺塔问题 初始状态: 思考:当盘子的个数是3的时候,大家写出移动顺序 移动的步骤: 3个盘子,从a到c 1.前面两个盘子,从a到b 1)把前面一个盘子,从a到c a->c 2)把第二个盘子 ...
随机推荐
- Kali-Linux-配置开发环境
本文主要讲解JDK.SDK.eclipse-adt.android studio.cpu模式TensorFlow 的安装配置.update:2019-08-30 03:31:46 JDK 当前系统jd ...
- kmp的简单应用
Smiling & Weeping ---- 我只为你一个人写过月亮 题目链接:P4824 [USACO15FEB] Censoring S - 洛谷 | 计算机科学教育新生态 (luogu. ...
- SpringBoot拦截器和动态代理有什么区别?
在 Spring Boot 中,拦截器和动态代理都是用来实现功能增强的,所以在很多时候,有人会认为拦截器的底层是通过动态代理实现的,所以本文就来盘点一下他们两的区别,以及拦截器的底层实现. 1.拦截器 ...
- SK 简化流行编程语言对 生成式AI 应用开发的支持
Semantic Kernel[1] 是一个将大型语言模型(LLM)与流行的编程语言相结合的SDK. Microsoft将Semantic Kernel(简称SK)称为轻量级SDK,支持AI LLM的 ...
- Gitbook Android App
最有用Ionic 2 做了一个Gitbook 混合app, 上线google play的时候提醒我触犯了假冒行为,可能是logo和名字问题吧,放弃: 上线腾讯被认为是h5 app,不是native a ...
- windows 网络模拟工具分享
[下载地址] Releases · jagt/clumsy · GitHub [介绍] 无需安装 无需篡改和代理 系统级限制,不针对单个程序,但可以针对单个IP 离线也可以限制,随停随用 界面简单 [ ...
- k8s添加节点报[WARNING SystemVerification]: missing optional cgroups: blkio
环境信息: ubuntu-master01 192.1681.195.128 ubuntu-work01 192.168.195.129 k8s版本 1.25.2 背景描述:初始环境是一个ma ...
- 删除软件 geek
下载链接 Geek Uninstaller_v1.5.1.162 -技术松鼠 (jishusongshu.com)
- 前端三件套系例之HTML——HTML文档结构、文档声明、主体结构标签、HEAD头部标签、meta元信息、Body内常用标签、6 其他了解
文章目录 HTML文档结构 1. 文档声明 2.主体结构标签 3.HEAD头部标签 4.meta元信息 5 Body内常用标签 5.1 基本标签(块级标签和内联标签) 5.2 div标签和span标签 ...
- poj2279
Mr. Young's Picture Permutations Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 5841 ...