1.前言
2. 递归的定义
递归其实是一种解决问题的方法,在C语言中,递归就是函数自己调用自己。
#include <stdio.h>
int main()
{
printf("hehen");
main();//main函数中⼜调⽤了main函数
return 0;
}
上述就是⼀个简单的递归程序,只不过上⾯的递归只是为了演示递归的基本形式,不是为了解决问题,代码最终也会陷⼊死递归,导致栈溢出。
递归的思想:
把⼀个大型复杂问题层层转化为⼀个与原问题相似,但规模较小的子问题来求解;直到子问题不能再被拆分,递归就结束了。所以递归的思考方式就是把大事化小的过程。
递归中的递就是递推的意思,归就是回归的意思,接下来慢慢来体会。
3. 递归的限制条件
4. 递归举例
4.1 求n的阶乘
计算n的阶乘(不考虑溢出),n的阶乘就是1~n的数字累积相乘。
4.1.1 分析和代码实现
将5的阶乘分成4的阶乘乘5;
将4的阶乘分成3的阶乘乘4;
将3的阶乘分成2的阶乘乘3;
将2的阶乘分成1的阶乘乘2;
这样的思路就是把⼀个较大的问题,转换为⼀个与原问题相似,但规模较小的问题来求解的。直到n是1或者0时,不再拆解
最终将n的阶乘就写成n*(n-1)!
直到n是1或者0时,不再拆解
如果将阶乘写成一个函数Fact(n)
,
那么Fact(n)=n*Fact(n-1)
再稍微分析一下,当 n<=1 的时候,n的阶乘是1,其余n的阶乘都是可以通过上述公式计算。
n的阶乘的递归公式如下:
那我们就可以写出函数Fact
求n的阶乘,假设Fact(n)
就是求n的阶乘,那么Fact(n-1)
就是求n-1的阶乘,函数如下:
int Fact(int n)
{
if(n<=0)
return 1;
else
return n*Fact(n-1);
}
来测试一下:
int Fact(int n)
{
if(n<=0)
return 1;
else
return n*Fact(n-1);
}
int main()
{
int n = 0;
scanf("%d", &n);
int ret = Fact(n);
printf("%dn", ret);
return 0;
}
4.1.2 画图演示
蓝色是递推的过程,此时并没有开始相乘。
而红色是回归的过程,此时回归时相乘。
4.2 顺序打印一个整数的每一位
4.2.1 分析和代码实现
这个题目,放在我们面前,首先想到的是,怎么得到这个数的每一位呢?
1234%10就能得到4,然后1234/10得到123,这就相当于去掉了4;
然后继续对123%10,就得到了3,再除10去掉3,以此类推;
不断的 %10 和 10 操作,直到1234的每⼀位都得到;
但是这里有个问题就是得到的数字顺序是倒着的。
但是我们有了灵感,我们发现其实⼀个数字的最低位是最容易得到的,通过%10就能得到
那我们假设想写⼀个函数Print来打印n的每⼀位,如下表示:
Print(n)
如果n是1234,那表⽰为
Print(1234) //打印1234的每⼀位
其中1234中的4可以通过%10得到,那么
Print(1234)就可以拆分为两步:
以此类推下去,就有
Print(1234)
==>Print(123) + printf(4)
==>Print(12) + printf(3)
==>Print(1) + printf(2)
==>printf(1)
直到被打印的数字变成⼀位数的时候,就不需要再拆分,递归结束。
那么代码就是
void Print(int n)
{
if (n > 9)
Print(n/10);
printf("%d ", n % 10);
}
int main()
{
int n = 0;
scanf("%d", &n);//1234
Print(n);
return 0;
}
在这个解题的过程中,我们就是使用了大事化小的思路
把Print(1234)
打印1234每一位,拆解为首先Print(123)
打印123的每⼀位,再打印得到的4
把Print(123)
打印123每一位,拆解为首先Print(12)
打印12的每一位,再打印得到的3
直到Print
打印的是一位数,直接打印就行。
4.2.2 画图推演
4.3 求第n个斐波那契数
斐波那契数列前两项都是1,后面的是前面两项的和。
斐波那契数的问题通过是使用递归的形式描述的公式。
看到这公式,很容易诱导我们将代码写成递归的形式,如下所示:
int Fib(int n)
{
if (n <= 2)
return 1;
else
return Fib(n - 1) + Fib(n - 2);
}
int main()
{
int n = 0;
scanf("%d", &n);
int ret = Fib(n);
printf("%dn", ret);
return 0;
}
来测试一下:
当我们n输⼊为50的时候,需要很长时间才能算出结果,这个计算所花费的时间,是我们很难接受的,这也说明递归的写法是非常常低效的,那是为什么呢?
要计算50就要先计算49和48,要计算49就要计算48和47,要计算48就要计算47和46,…一直这个下去,浪费时间重复计算。
那么除了递归还有其它的方式吗?
此时就要介绍迭代。
5. 递归与迭代
递归是一种很好的编程技巧,但是很多技巧一样,也是可能被误用的,就像举例1⼀样,看到推导的公式,很容易就被写成递归的形式:
int Fact(int n)
{
if(n<=0)
return 1;
else
return n*Fact(n-1);
}
Fact函数是可以产生正确的结果,但是在递归函数调用的过程中涉及一些运行时的开销。
在C语言中每一次函数调用,都要需要为本次函数调用在栈区申请⼀块内存空间来保存函数调用期间的各种局部变量的值,这块空间被称为运行时堆栈,或者函数栈帧。
函数不返回,函数对应的栈帧空间就⼀直占用,所以如果函数调用中存在递归调用的话,每⼀次递归函数调用都会开辟属于自己的栈帧空间,直到函数递归不再继续,开始回归,才逐层释放栈帧空间。
所以如果采用函数递归的方式完成代码,递归层次太深,就会浪费太多的栈帧空间,也可能引起栈溢出(stack over flow)的问题。
所以如果不想使用递归就得想其他的办法,通常就是迭代的方法(通常就是循环的方法)。
比如:计算n的阶乘,也是可以产生1~n的数字累计乘在⼀起的。
int main()
{
int n = 0;
scanf("%d", &n);
int i = 0;
int ret = 1;
for (i = 1; i <= n; i++)
{
ret = ret * i;
}
printf("%dn", ret);
return 0;
}
注意:
- 如果一个问题使用递归的方式去写代码,是非常方便的,简单的写出的代码是没有明显缺陷的,这个时候使用递归就可以
- 如果使用递归写的代码, 是存在明显的缺陷的
比如: 栈溢出, 效率低下等
这时候必须考虑其他方式, 比如:迭代
5.1 迭代求第n个斐波那契数
前两个数不需要计算,假设第一个用a记录,第二个用b记录。当n大于2时就要实现前面两个数字,就要相加,然后将a和b都向后挪,也就是将b的值给a,c的值给b,然后再执行a+b,每执行一次n都要减减一下。
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;
}
int main()
{
int n = 0;
scanf("%d", &n);
int ret = Fib(n);
printf("%dn", ret);
return 0;
}
有问题请指出,大家一起进步!
原文地址:https://blog.csdn.net/zxctsclrjjjcph/article/details/134793826
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.7code.cn/show_43342.html
如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱:suwngjj01@126.com进行投诉反馈,一经查实,立即删除!