算法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)把第二个盘子 ...
随机推荐
- Nomad 系列-快速上手
系列文章 Nomad 系列文章 Nomad 重要术语 Nomad 安装设置相关术语 agent - 代理.Agent 是在 Server(服务器) 或 Client(客户端) 模式下运行的 Nomad ...
- KRPANO太阳光插件
KRPano太阳光插件可以在全景项目中添加太阳光特效,如下图所示: 同时,该插件支持可视化编辑 使用说明 1.下载插件,把插件放入skin文件夹里面 2.在tour.xml文件中,添加下面的插件引用 ...
- numpy中矩阵的逆,求解,特征值,特征向量
逆:numpy.linalg.inv() # 求矩阵的逆import numpy as npa=np.mat('1 0;0 1')#生成一个矩阵print(type(a))b=np.linalg.in ...
- Skynet通讯遇到的奇怪问题
问题 最近在做一个内部通讯的服务器, 用的自带的gateserver和socketchannel做通讯, 在使用skynet.unpack或者string.unpack("XXXX" ...
- 文心一言 VS 讯飞星火 VS chatgpt (107)-- 算法导论10.1 5题
五.用go语言,栈插入和删除元素只能在同一端进行,队列的插入操作和删除操作分别在两端进行,与它们不同的,有一种双端队列(deque),其插入和删除操作都可以在两端进行.写出4个时间均为 O(1)的过程 ...
- oj练习题 数字 eval 整数 int ???
s = input()if 'hello world!' == s.casefold(): print("Yes")else: print("No") A+B问 ...
- Gitlab Server
Gitlab 基本概述 1.什么是Gitlab ? Gitlab是一个开源分布式的版本控制系统. Ruby语言开发完成. Gitlab主要实现的功能.管理项目源代码.对源代码进行版本控制.以及代码复用 ...
- SSL证书链及使用
什么是证书链 证书链简单来说是域名钥证书.CA公钥.根证书形成的一个颁发链条,属于公钥的一部分. 更白话一点,就是证书链文件包含一系列CA机构公钥的证书. 证书链格式 一般证书链格式是.chain,证 ...
- SQL改写案例1
一开发哥们找我改写SQL,他写的逻辑始终不对,安排! -- 他写的SQL: -- order_id 是主键 with a as ( select str_to_date(regist_time,'%Y ...
- 历时三年,写的一本数据结构与算法pdf,开源了!
前言 大家好,我是bigsai,很早就在写博客,将文章整理成了一个pdf,并且开源到github上! 自己写东西断断续续也不少时间了,也写了不少东西(虽然是偏向小白),这个其实花费的时间还是比较多的, ...