C语言递归练习——汉诺塔

  • 我想大家可能都听过汉诺塔这个益智游戏,或者也都玩过
  • 今天我们就借助递归思想来解汉诺塔
  • 在此之前,我们首先要了解什么是递归,
  • 可以参考这篇博客函数递归

1. 函数递归

1.1 概念

  • 递归可以拆成两部分,即递延和回归,那什么是递归呢?简单来说,就是函数自己调用自己。

1.2 条件

  • 如果不加任何条件,让函数一直调用自己, 因为每当我们调用函数的时候都会在栈区分配空间,如果一直调用,总会有空间不足的情况,这就会出现栈溢出的问题,
  • 所以为了避免栈溢出,我们在写递归的时候需要注意两个问题,第一个就是递归需要限制条件,即满足一定条件才能开始递归,第二个就是递归过程必须越来越接近限制的条件
  • 接下来我们用具体的代码来解释以下
#include <stdio.h>

int print2(char* str)		//计算字符长度
{
	if (*str != '\0')
	{
		return (print2(str + 1) + 1);  //递归
	}
	else
	{
		return 0;
	}
}

int main()	
{
	char arr1[10] = "";
	scanf("%s", arr1);
	printf("%d", print2(arr1));
	return 0;
}
  • 这里定义了一个计算字符长度的函数,可以看到在这个函数的内部还调用了自己
  • 这就是典型的递归函数
  • 那这个递归过程是怎么样的呢?
  • 接下来就让我们分析一下

1.3 分析

  • 首先定义了一个arr1的字符数组,然后输入,之后在输出print2函数的返回值
  • print2的参数是指针,即数组的首地址
  • 假设我们要求的字符arr1 = “abc”;
  • 那么实际数组中存储的是a b c \0
  • 那么我们只要往后遍历直到遇到 \0 停止
  • 即可计算出长度

在这里插入图片描述

  • 这里我用图来解释一下,首先我们知道代码是一行一行执行的,在一行上加法的同优先级的执行顺序是从左往右
  • 那么从 1 开始往下执行,到 2 开始判断这个位置是不是 \0 ,如果不是就再次调用 print2 ,只不过再次调用的时候要注意这次判断的是后面一个字符了,如果这里的 str + 1 换成 str ,就会出现栈溢出的问题,一直判断的都是 a 这一个字符
  • 接着到 3 ,注意!此时我们仍然在执行 print2(str + 1) ,所以后面的 +1还没有执行,此时我们在判断 b ,发现也不是 \0 那么接着进行 4 ,开始判断 c ,也不是 \0 ,接着进行 5 即判断 \0 ,此时在 print2(\0) 中,我们执行else返回 0,此时 print2( c ),执行完了,接着进行 7 即执行 print2( c ) 后面的+1,此时返回 1,接着 print2(b) 执行完了,接着进行 8 即再+1,此时返回 2 ,那么 print2(str + 1) 也执行完了,最后进行 9 即再+1,所以最后的返回值为 3,最后,最外层的 if 语句执行完了,进行 10 即最外层的print2函数执行完。
  • 这就是递归的整个过程了

2. 汉诺塔

  • 有了递归的思想后,我们再来了解一下什么是汉诺塔,
  • 有兴趣的同学可以去玩一下,汉诺塔游戏

2.1游戏规则

在这里插入图片描述

  • 规则是这样的:
  • 有 n 个盘子放在 A ,且从下往上越来越小,现在需要把A上的所有盘子都移到 C 去,且在此过程中不论是在 A ,B 还是 C 上,盘子的大小都是下面大,上面小不能倒过来

2.2游戏的分析

  • 下面我们先来想想如果解决上面三个盘子的问题,
  • 首先我们要把最大盘子移到 C 的话,这个盘子肯定也是在最底下的,所以我们可以先把上面的两个盘子通过 C 移动到 B,再把最大的直接移动到 C,
  • 可是两个盘子没法一块移动呀,那么我们可以先把最上面的移动到 C ,这样就可以把中间的移动到 B 了,然后再把最小的从 C 移动到 B ,这样最上面的两个就移动到 B 了,然后再把最大的直接移动到C即可,
  • 同理接着把最小的移到 A ,然后把下面的移动到 C ,最后再把最小的移动到 C 即可完成
  • 用文字解释太抽象了,接下来再用图来描述一下整个过程。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2.3代码实现

  • 根据上面的思路,我们试试用代码来实现,
  • 首先定义一个函数,用来移动盘子,参数是盘子的总数,起始位置 start, 中间位置 temp,和最终放置的位置 end
void yidong(int n, char start, char temp, char end)//将start通过temp移动到end
{
	if (n == 1)
	{
		printf("%c-->%c\n", start, end);
	}
	else
	{
		yidong(n - 1, start, end, temp);
		printf("%c-->%c\n", start, end);
		yidong(n - 1, temp, start, end);
	}
}
  • 首先当只有一个盘子的时候,直接将其从 start 位置移动到 end 位置即可
  • 当有 n 个盘子的时候,可以先把上面的 n-1 个盘子先通过 end 移动到 temp 然后再将最底下的从 start 移动到 end ,之后再只要把在 temp 上的 n-1 的盘子再移动到 end 即可,那么可以再先把上面的 n-2 个盘子先通过 end 移动到 start 上,再把第 n-1 个盘子直接移到 end 即可
  • 这样代码的思路就完成了,首先判断 n 是否为 1 ,如果不是就把上 n-1 个先通过 end 移动到 temp ,所以参数的顺序为 start, end, temp,然后再把 start 上的一个移动到 end ,最后再把 temp 上,上面的 n-2 个盘子通过 end 移动到 start 上,所以参数的顺序为 temp, start, end
  • 这样代码就完成了
  • 最后再把main函数的代码编写即可
#include <stdio.h>

void yidong(int n, char start, char temp, char end)//将start通过temp移动到end
{
	if (n == 1)
	{
		printf("%c-->%c\n", start, end);
	}
	else
	{
		yidong(n - 1, start, end, temp);
		printf("%c-->%c\n", start, end);
		yidong(n - 1, temp, start, end);
	}
	
int main()
{
	int n = 0;
	scanf("%d", &n);
	yidong(n,'A','B','C');
	return 0;
}//汉诺塔游戏
}

最后,
如果上述代码或者表述有问题,
欢迎一起交流

  • 29
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

看落日的YT

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

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

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

打赏作者

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

抵扣说明:

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

余额充值