《数据结构与课程设计》
大作业
项目名称 汉诺塔问题的递归和非递归实现
学 院 信息与通信工程学院
年级专业 20级智能科学与技术
姓 名 孙成
学 号 20203101694
完成时间 2021-6-5
指导教师 周铁老师
开 课 时 间 2020 至 2021 学年第 二 学期
目录
一、背景介绍
二、需求分析
1、项目要求
2、运行环境
三、技术路线
1、递归算法实现
2、非递归算法实现
3、测试分析
四、个人心得
五、参考文献
一、背景介绍
汉诺塔问题是源于印度一个古老传说的益智玩具。大梵天创造世界的时候做了三根金刚石柱子,在一根柱子上从下往上按照大小顺序摞着64片黄金圆盘。大梵天命令婆罗门把圆盘从下面开始按大小顺序重新摆放在另一根柱子上。并且规定,在小圆盘上不能放大圆盘,在三根柱子之间一次只能移动一个圆盘。如下图:
如果考虑一下把64片金盘,由一根柱子上移到另一根柱子上,并且始终保持上小下大的顺序。这需要多少次移动呢?这里需要递归的方法。假设有n片,移动最少次数是f(n).显然f(1)=1,f(2)=3,f(3)=7,根据数学归纳法可以得出f(k+1)=2*f(k)+1。
此后不难证明
二、需求分析
1、项目要求
1.用递归调用的方法解实现问题的解。
2.自己设置一个工作栈,用非递归方法求解该问题。
3.程序要求用户输入初始圆盘数
4.输出所有移动过程和总的移动次数
2、运行环境
macOS Big Sur 版本11.4
MacBook Pro (16-inch, 2019)
处理器2.6GHz六核Intel Core i7
内存16 GB 2667 MHz DDR4
图形卡Intel UHD Graphics 630 1536 MB
Xcode Version 12.5 (12E262)
三、技术路线
1、递归算法实现
①代码部分
- #include <stdio.h>
- #include <math.h>
- int main()
- { //定义汉诺塔函数的递归算法,其中n表示盘子的个数,one表示左起第一个柱子,two表示左起第二个柱子,three表示左起第三个柱子
- void hanoi(int n,char one,char two,char three);
- int m; //定义盘子的个数
- double t; //t-1为总移动次数
- printf("输入盘子个数:");
- scanf("%d",&m);
- printf("移动 %d 个盘子的步骤是:\n",m);
- hanoi(m,'A','B','C');
- t=pow(2, m); //表示2的m次方
- printf("一共移动了%.0lf次\n",t-1); //输出移动次数
- return 0;
- }
- void hanoi(int n,char one,char two,char three) //最后都移动到最右侧的柱子
- {
- if(n==1){
- printf("%c柱最上面的盘子-->%c柱\n",one,three); //如果只有一个盘,将one柱最上面的一个盘子移到three柱(最右柱)即可
- }
- else //后面有图解
- {
- hanoi(n-1,one,three,two);
- printf("%c柱最上面的盘子-->%c柱\n",one,three);
- hanoi(n-1,two,one,three);
- }
- }
②手写原理解释
2、非递归算法实现
①汉诺塔的非递归算法描述如下:
首先把三根柱子按顺序排成品字型,把所有的圆盘按从大到小的顺序放在柱子A上,根据圆盘的数量确定柱子的排放顺序:
若n为偶数,按顺时针方向依次摆放 A B C;
若n为奇数,按顺时针方向依次摆放 A C B。
(1)按顺时针方向把圆盘1从现在的柱子移动到下一根柱子,即当n为偶数时,若圆盘1在柱子A,则把它移动到B;若圆盘1在柱子B,则把它移动到C;若圆盘1在柱子C,则把它移动到A。
(2)接着,把另外两根柱子上可以移动的圆盘移动到新的柱子上。即把非空柱子上的圆盘移动到空柱子上,当两根柱子都非空时,移动较小的圆盘。这一步没有明确规定移动哪个圆盘,你可能以为会有多种可能性,其实不然,可实施的行动是唯一的。
(3)反复进行(1)(2)操作,最后就能按规定完成汉诺塔的移动。
只要轮流进行两步操作就可以了,结果非常简单,就是按照移动规则向一个方向移动金片:
如3阶汉诺塔的移动:A→C,A→B,C→B,A→C,B→A,B→C,A→C
②该算法的实现代码如下:
- #include <stdio.h>
- #include <math.h>
- #define MAXNUM 64 //定义最大盘子数为64
- typedef struct //hanoi结构体
- {
- int no; //no为hanoi标志
- int ns; //ns为hanoi盘子序号
- char x,y,z; //x,y,z分别为三个hanoi塔座
- }Stack;
- void Hanoi(Stack st[],int n,char a,char b,char c) //hanoi移动程序
- {
- int top=1; //top为栈顶置针
- int n1; //n1为当前盘子序号
- char a1,b1,c1; //a1,b1,c1为临时塔座
- st[top].no=1;
- st[top].ns=n;
- st[top].x=a;
- st[top].y=b;
- st[top].z=c;
- while(top>0)
- {
- if(st[top].no==1)
- {
- n1=st[top].ns; //退栈hanoi(n,x,y,z)
- a1=st[top].x;
- b1=st[top].y;
- c1=st[top].z;
- top--;
- top++; //将hanoi(n-1,x,y,z)入栈
- st[top].no=1;
- st[top].ns=n1-1;
- st[top].x=b1;
- st[top].y=a1;
- st[top].z=c1;
- top++; //将第n个圆盘从x移到z
- st[top].no=0;
- st[top].ns=n1;
- st[top].x=a1;
- st[top].y=c1;
- top++; //hanoi(n-1,x,y,z)入栈
- st[top].no=1;
- st[top].ns=n1-1;
- st[top].x=a1;
- st[top].y=c1;
- st[top].z=b1;
- }
- while(top>0 &&(st[top].no==0||st[top].ns==1))
- {
- if(top>0 && st[top].no==0) //将第n个圆盘从x移到z并退栈
- {
- printf("\t将第%d个盘片从%c移动到%c\n",st[top].ns,st[top].x,st[top].y);
- top--;
- }
- if(top>0&&st[top].ns==1) //退栈hanoi(1,x,y,z)
- {
- printf("\t将第%d个盘片从%c移动到%c\n",st[top].ns,st[top].x,st[top].z);
- top--;
- }
- }
- }
- }
- int main(int argc,char* argv[])
- {
- int n=-1;
- double t; //t-1为总移动次数
- Stack st [MAXNUM] ;
- printf("请输入汉诺塔中盘子个数:");
- scanf("%d",&n);
- t=pow(2, n); //表示2的n次方
- printf("hanoi(%d)的移动程为:\n",n);
- Hanoi (st, n, 'x','y','z');
- printf("一共移动了%.0lf次\n",t-1); //输出移动次数
- return 0;
- }
3、测试分析(取盘子个数分别为2、3时的结果)
①递归算法的运行结果
⑴盘子个数为2
⑵盘子个数为3
②非递归算法的运行结果
⑴盘子个数为2
⑵盘子个数为3
四、个人心得
在老师布置了大作业的选题以后,我便开始选择题目,在浏览了七十多题后,我最终选择了汉诺塔问题作为此次数据结构课程设计的大作业。汉诺塔问题更像是一道数学题,这对我来说是擅长的。定题后,我便开始后面的设计。
首先,由于我们是上学期学C语言,半个学期过后对它里面的各项功能和函数库以及消息映射机制有很多遗忘。开始写递归的程序后,花费了我很多时间调试,于是我开始反思,在还没有很仔细研究书上有关内容的情况下,就匆忙写程序,这个是错误的,这个时候我选择停下来,整理思路,复习学过的C语言语法和相关函数。
在写完递归程序后,我打开了上学期的谭浩强先生的《C语言程序设计》,参考书上的汉诺塔问题的递归形式函数,并对已完成的代码作出了修改。
在写非递归函数时,首先遇到的难题是如何构建框架以及,如何说明每一步的移动过程,在检阅CSDN上的相关文章后,顿时,好像一下子从暗无天日的盲目中找到了方向,心中一下子开阔起来。
但是这个过程还是出现了曲折,网上的都是基于C++实现的,相关的代码我不能直接套用,针对这个问题,我首先是检阅C++相关的语法,接着学习栈的使用方法,在进行改码时,还是出现了很多错误,在一遍遍的修改查资料后,还是不能一下子就很快地将程序调出来,这个时候我们想到了学长,在学长的指导下,我们很快地找到了问题的症结:C++是面向对象的,而C是解决性能的,且主要学习的是指针、内存、数据类型,本质是理解计算机系统结构。于是我参考《数据结构课程设计》这本书,将相关的代码进行修改。
接下来的过程就很轻松,一遍遍的运行代码,一遍遍的优化代码,最终获得了让人满意的汉诺塔非递归方式用C语言实现的代码。
最后一点最重要的体会就是:要学会查找资料,要学会请教他人,学会坚持和钻研。不要怕!不要悔!
五、参考文献
代码高亮处理http://www.codeinword.com
《数据结构课程设计》滕国文、
《C语言程序设计》谭浩强
数据结构课程设计2020-2021.rar