递归实现汉诺塔(萌新)

第一遍写的时候没保存被删掉了,两小时白费,一切重来,希望如果有帮助的话能留下一个赞,真的感谢!

 




前言

本文章为刚刚接触C语言的大一新生自创,主要介绍汉诺塔问题及其解决方法




一、汉诺塔是什么?

汉诺塔(Tower of Hanoi),又称河内塔,是一个源于印度古老传说的益智玩具大梵天创造世界的时候做了三根金刚石柱子,在一根柱子上从下往上按照大小顺序摞着64片黄金圆盘。大梵天命令婆罗门把圆盘从下面开始按大小顺序重新摆放在另一根柱子上。并且规定,在小圆盘上不能放大圆盘,在三根柱子之间一次只能移动一个圆盘。——源自百度百科

衍生问题:

三根相邻的柱子,标号为A,B,C,A柱子上从下到上按金字塔状叠放着n个不同大小的圆盘(从下到上逐渐变小),要把所有盘子一个一个移动到柱子C上,并且每次移动同一根柱子上都不能出现大盘子在小盘子上方,请问至少需要多少次移动



二、汉诺塔问题的思考过程以及代码实现



1.递归的思想就是由大化小,因此这个问题我们先简单化,从较简单的情况开始思考

n=1:

只需要将A柱上的盘子移动到C柱

n=2:

第一步:将蓝盘由A移动到B

第二步:将绿盘由A移动到C

第三步:将蓝盘由B移动到C

n=3:

 第一步:将红盘由A移动到C

第二步:将蓝盘由A移动到B

 第三步:将红盘由C移动到B

注意,此时A柱上只有最大的绿盘,C柱上没有任何圆盘,故可以直接将绿盘由A移动到C 

第四步:将绿盘由A移动到C

仔细观察,这一步的图和第一步结束后的图有些相似,那么接下来的步骤是否可以理解为一种对称呢?

 第五步:将红盘由B移动到A

 第六步:将蓝盘由B移动到C

 第七步:将红盘由A移动到C

 综上,可以总结出一个数学规律,需要移动的总步数N=2^n-1,根据上述步骤,我将他们分成

2[2^(n-1)-1]+1,如何理解呢?首先因为中间必定有一步需要将最大的盘由A一步移动到C,故结果一定为奇数,而前后则可以分为对称的两大步,每一大步包含2^(n-1)-1小步。

所以伪代码如下(n>1):

1.将除最大的盘(共n-1个盘)由A柱借助C柱移动到B柱(不要去思考如何实现具体这一步,这是计算机需要思考的事情

2.将最大的盘由A柱直接移动到C柱

3.将在B柱上的n-1个盘借助A柱移动到C柱

结束

可以这么理解,如果假定最大的盘是公司的CEO,CEO需要办成一件事情的时候需要吩咐下属,没有下属的时候(n=1),CEO需要自己从A部门走到C部门,有下属的时候(n-1个下属),CEO就要吩咐下属先从A部门移动到B部门候命,自己走捷径(直接从A到C)来解决问题,再让n-1个下属从B部门想办法到C部门,CEO不需要考虑下属们是如何到达C部门的,下属只需要给CEO让路,永远让CEO走最近的路(好好理解!!

代码如下:(C语言)

#define _CRT_SECURE_NO_WARNINGS 1     //VS2019会报错,输入这一行就不会报错了
#include <stdio.h>
int count = 0;//全局变量count用来记录步数
void hannuota(int n, char A, char B, char C)
{
	if (n == 1)
	{
		printf("第%d次从%c--->%c\n",++count,A, C);
	}
	else
	{
		hannuota(n - 1, 'A', 'C', 'B');
		printf("第%d次从%c--->%c\n",++count,A, C);
		hannuota(n - 1, 'B', 'A', 'C');
	}
}
int main()
{
	int n = 0;
		char A, B, C;
	printf("请输入汉诺塔层数:");
	scanf("%d", &n);
	printf("移动的步骤如下:\n");
	hannuota(n,'A', 'B', 'C');
	printf("一共移动了%d次\n ", count);
	return 0;
}

三、一些可能的问题(我遇到的)(大佬忽略,纯属小白自己遇到的问题)

1.最重要的写在最前面,对刚接触递归的我们来说,这道汉诺塔的递归每一次调用函数时,参数的顺序发生了改变,如第一次进入函数时用字符A接收'A',字符B接收'B',字符C接收‘C’,第二次在else语句中再次调用函数时,则是用A接收刚刚A的值,字符B接收刚刚C的值,字符C接收刚刚B的值,这也就是为什么除第一次以外再调用函数时的参数不需要再添加单引号'',因为第一次调用函数后接收值均已带上了单引号且在递归过程中一直保留,以此往复,不断调用函数传参改值,因此每次输出结果时只需要写从A到C。(表达不清,能理解吗,5555我这表达能力)

2.记录步数时最好用全局变量,如果用局部变量需要函数返回值,略麻烦,也可以考虑用static静态变量

3.全局变量初始值为0且输出结果时为++count(先将count的值+1再使用),而不是count++(先使用count的值再讲count的值+1),这样可以保证步数是对的,如果将count的值初始化为1且使用count++的时候会导致总步数多1(选择语句多执行一次)




总结

从接触问题到思考大概花费3小时,完善代码debug又花费将近两个小时,写博客又花费了2个小时,这是第一次接触递归的每一个CS人必须跨越的一道坎,文章把我在学习过程中遇到的问题都基本都涵盖了,如果还有问题欢迎在评论区讨论(尤其是问题中的第一点,可能是我太菜了,因为忽略这一点导致卡在这里一个多小时),如果有帮助希望能点个赞支持一下!也非常非常欢迎各位大佬评论区指正点拨启发我!非常感谢!

以后会记录自己在学习编程过程的点点滴滴,欢迎关注一起回访鸭!

  • 104
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 23
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 23
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

printf("雷猴");

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值