目录
🔥个人主页:艾莉丝努力练剑
🍓专栏传送门:《C语言》
🍉学习方向:C/C++方向
⭐️人生格言:为天地立心,为生民立命,为往圣继绝学,为万世开太平
前言:前面几篇文章介绍了c语言的一些知识,包括循环、数组、函数、VS实用调试技巧等,在这篇文章中,我将介绍函数递归的一些重要知识点!
一、递归的概念及其思想
(一)递归是什么
递归其实是一种解决问题的方法,在C语言中,递归就是函数自己调用自己。
我们写一个史上最简单的C语言递归程序代码,只不过下面的递归只是为了演示递归的基本形式,不是为了解决问题,代码最终也会陷入死递归,导致栈溢出(Stack overflow)。因此仅作展示:
#include <stdio.h>
int main()
{
printf("hehe\n");
main();
//main函数中⼜调⽤了main函数
return 0;
}
会报出下面的警告:
(二)递归的思想
把一个大型复杂问题层层转化为一个与原问题相似,但规模较小的子问题来求解;直到子问题不能再被拆分,递归就结束了。所以递归的思考方式就是把大事化小的过程。说到这里,博主联想到了枫桥经验,和递归思想有异曲同工之妙!只不过枫桥经验是“发动和依靠群众,坚持矛盾不上交,就地解决,实现捕人少,治安好”的基层社会治理经验,大事化小小事化了啦。好啦,言归正传:递归中的递就是递推的意思,归就是回归的意思,结合知识点,友友们可以体会一下!
(三)递归的限制条件
递归在书写的时候,有两个必要条件:
(1)递归存在限制条件,当满足这个限制条件的时候,递归便不再继续;
(2)每次递归调用之后越来越接近这个限制条件。
二、递归举例
这里咱们举两个实例:一个是求n的阶乘,另一个就是顺序打印一个整数的每一位。
(一)求n的阶乘
咱们先分析出来n的阶乘的公式:n!= n ∗ (n − 1)!
这样咱们已经可以写出n的阶乘的递归公式了:
代码展现:
#define _CRT_SECURE_NO_WARNINGS 1
int Fact(int n)
{
if (n == 0)
return 1;
else
return n * Fact(n - 1);
}
//测试
#include <stdio.h>
int main()
{
int n = 0;
scanf("%d", &n);
int ret = Fact(n);
printf("%d\n", ret);
return 0;
}
咱们测试一下,看看代码有没有按照我们的想法走,这里我们输入一个4,按理说4的阶乘是24,这里应该输出24,咱们来看看是不是这样:
画图推演一下:
(二)顺序打印一个整数的每一位
我们发现其实一个数字的最低位是最容易得到的,通过%10就能得到,那我们假设想写一个函数Print来打印n的每一位不就可以了嘛:
Print(n)
如果n是1234,那表⽰为
Print(1234) //打印1234的每⼀位
其中1234中的4可以通过%10得到,那么
Print(1234)就可以拆分为两步:
1. Print(1234/10) //打印123的每⼀位
2. printf(1234%10) //打印4
完成上述2步,那就完成了1234每⼀位的打印
那么Print(123)⼜可以拆分为Print(123/10) + printf(123%10)
并且以此类推,我们可以得到:
Print(1234)
==>Print(123) + printf(4)
==>Print(12) + printf(3)
==>Print(1) + printf(2)
==>printf(1)
代码展现:
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
void Print(int n)
{
if (n > 9)
{
Print(n / 10);
}
printf("%d ", n % 10);
}
int main()
{
int m = 0;
scanf("%d", &m);
Print(m);
return 0;
}
咱们这里随意输入几个数字看看效果:
咱们这里以1234为例画图推演一下:
三、递归与迭代
要对比这两种思想,我们需要结合实际例子,这里咱们就以求第n个斐波那契数为例:
(一)递归
实话实说,递归方法是不适合用来求解像求第n个斐波那契数这样的问题的,不过,用递归的形式描述是没什么问题的,而且会比用迭代描述简单的多:
咱们来看看用递归思想写出来的代码:
#define _CRT_SECURE_NO_WARNINGS 1
int Fib(int n)
{
if (n <= 2)
return 1;
else
return Fib(n - 1) + Fib(n - 2);
}
//测试代码
#include <stdio.h>
int main()
{
int n = 0;
scanf("%d", &n);
int ret = Fib(n);
printf("%d\n", ret);
return 0;
}
当我们n输入为50的时候,需要很长时间才能算出结果,这个计算所花费的时间,是我们很难接受的 ,咱们用二叉数来看一下第50个斐波那契数:
二叉数看第50个斐波那契数,有大量重复。
咱们把这个数再缩小一点点,还是有大量重复,例如,这里仅仅只是计算第40位斐波那契数就重复计算了3000多万次:
(二)迭代
这里用迭代的方法去实现这个代码效率就高出很多了:
#define _CRT_SECURE_NO_WARNINGS 1
int Fib(int n)
{
int a = 1;
int b = 1;
int c = 1;
while (n > 2)
{
c = a + b;
a = b;
b = c;
n--;
}
return c;
}
我们知道斐波那契数的前2个数都为1,然后前2个数相加就是第3个数,那么我们从前往后,从小到大计算就行了。
用迭代(简单理解为循环)写就好了,效率高,第50000个也是一瞬间的事,只不过结果是错的,超过了int的范围(一个整型放不下太大的数字)
关于斐波那契数问题,咱们可以再稍微拓展一下:比如青蛙跳台阶问题也是斐波那契数问题,大家可以去了解一下,这里推荐一个博主的文章了解青蛙跳台阶问题:详解青蛙跳台阶问题。
结语
往期回顾:
掌握函数(一):库函数与自定义函数、形参与实参、return语句
掌握分支循环(二):三种循环、break和continue语句、循环的嵌套以及 goto 语句
掌握分支与循环(一):if语句、三种操作符、switch语句
本篇文章就到此结束了,本文为友友们分享了一些函数递归的一些重要知识点,如果友友们有补充的话欢迎在评论区留言交流!下一期我们将介绍操作符相关的一些重要知识点,感谢友友们的关注与支持!