- 我想大家可能都听过汉诺塔这个益智游戏,或者也都玩过
- 今天我们就借助递归思想来解汉诺塔
- 在此之前,我们首先要了解什么是递归,
- 可以参考这篇博客函数递归
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;
}//汉诺塔游戏
}
最后,
如果上述代码或者表述有问题,
欢迎一起交流