【C语言】玩转递归——学好递归,你需要掌握的知识

{
//斐波那契数 1 1 2 3 5 8 13 21 34 51…,除前两位外,后一个数的值等于前两位相加
int n = 0;
printf(“请输入你要查找的斐波那契数:”);
scanf(“%d”, &n);
int ret=Fib(n);
printf(“你好,你要需要的值是:%d\n”, ret);
return 0;
}


![在这里插入图片描述](https://img-blog.csdnimg.cn/aa02c8151c184f3c8dfca1d04fd51f7c.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA5rOi6aOO5byg5LiJ,size_20,color_FFFFFF,t_70,g_se,x_16)所以,我们可以看到,所谓递归,其实就是一个函数里面调用函数自己本身。具体怎样调用的,我们下面再讲。


## 二、 递归的两个必要条件



> 
> 1、存在限制条件,当满足这个限制条件的时候,递归便不再继续。  
>  2、 每次递归调用之后越来越接近这个限制条件。
> 
> 
> 


分析之后,我们可以得出两个点



> 
> 1、结束条件  
>  2、逼近条件
> 
> 
> 


我们在使用递归的时候,需要满足这两个条件。


总结起来四个字——***大事化小***


继续举斐波那契数的例子:


![在这里插入图片描述](https://img-blog.csdnimg.cn/a755d20a12f242ed96e5ee7660006301.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA5rOi6aOO5byg5LiJ,size_20,color_FFFFFF,t_70,g_se,x_16)


## 三、递归是怎样运行的


我们通过一道题目来讲解。



> 
> 题目: 递归实现n的k次方  
>  内容: 编写一个函数实现n的k次方,使用递归实现。
> 
> 
> 


【解决思路】  
 运用递归思路,我们只要找到递归结束条件和逼近条件。通过分析,我们可以画出下面这幅图。


![在这里插入图片描述](https://img-blog.csdnimg.cn/d17353d887a449988e7654e7df150ac8.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA5rOi6aOO5byg5LiJ,size_16,color_FFFFFF,t_70,g_se,x_16)


【代码实现】



#include <stdio.h>
double power(int n,int k)
{
if (k< 0)
{
k = -k;
return 1 / (n*power(n, k - 1));
}
else if (k == 0)
return 1;
else if (k>0)
{
return n * power(n, k - 1);
}
}
int main()
{
int n = 0;
int k = 0;
printf(“请输入一个整数:”);
scanf(“%d”, &n);
printf(“请输入要求的次方数:”);
scanf(“%d”, &k);
double ret=power(n,k);
printf(“%1f\n”, ret);
return 0;
}


【画图详解递归思路】


![请添加图片描述](https://img-blog.csdnimg.cn/4cf65c6cbe66450db01176f95cda2a88.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA5rOi6aOO5byg5LiJ,size_20,color_FFFFFF,t_70,g_se,x_16)通过图解,发现思路,我们\*\* 存在限制条件k,当满足这个限制条件的时候,递归便不再继续。  
 每次递归调用之后越来越接近这个限制条件。\*\*之后输出的时候就反过来回去。


这个就是递归的思路。


## 四、迭代与递归


不知大家有没有认真思考过上面的求斐波那契数的代码,它有什么问题?


![在这里插入图片描述](https://img-blog.csdnimg.cn/dcb7f3735e2043c08bffdae3058a6f84.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA5rOi6aOO5byg5LiJ,size_20,color_FFFFFF,t_70,g_se,x_16)  
 如果我们这里求的是第50个斐波那契数呢?大家可以运行一下代码。可以发现,电脑运行了好久好久才算出结果,费时间。  
 如果求第10000个斐波那契数呢?程序就会崩溃。


为什么呢?  
 我们发现上面求斐波那契数的 Fib 函数 在调用的过程中很多计算其实在一直重复。  
 因为我们在调用这个函数的时候,除前两位外,后一个数的值等于前两位相加。这就导致了我们不断重复计算


如图:  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/7f7122b13ddb4871a42c50ca0ec81d4f.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA5rOi6aOO5byg5LiJ,size_20,color_FFFFFF,t_70,g_se,x_16) 我们可以看到,由于前两个数相加等于后一个数,前两个数相加等于后一个数,所以我们会不断产生重复的计算。就会造成计算量非常大,**效率极低**。


那我们如何改进呢?  
 我们程序存东西的时候,存放在栈区。  
 如图:  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/c16bc09045e948599a8b0326d692ddb6.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA5rOi6aOO5byg5LiJ,size_20,color_FFFFFF,t_70,g_se,x_16)


在调试 例子中的Fib函数的时候,如果你的参数比较大,那就会报错: `stack overflow(栈溢出) 这样的信息。  
 系统分配给程序的栈空间是有限的,但是如果出现了死循环,或者(死递归),这样有可能导致一直开辟栈空间,最终产生栈空间耗尽的情况,这样的现象我们称为栈溢出。


那如何解决上述的问题:



> 
> 1. 将递归改写成非递归。
> 2. 使用static对象替代non-static局部对象。在递归函数设计中,可以使用static对象替代nonstatic局 部对象(即栈对象),这不仅可以减少每次递归调用和返回时产生和释放nonstatic对象的开销,  
>  而且static对象还可以保存递归调用的中间状态,并且可为各个调用层所访问。
> 
> 
> 


这里我们介绍**迭代**。


什么是迭代呢?  
 【概念】



> 
> 迭代是重复反馈过程的活动,其目的通常是为了逼近所需目标或结果。每一次对过程的重复称为一次“迭代”,而每一次迭代得到的结果会作为下一次迭代的初始值。
> 
> 
> 


借用网上的图片来说明**(侵删)**  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/2e3950d7e68e4fe2ad9c7540ad6906b5.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA5rOi6aOO5byg5LiJ,size_20,color_FFFFFF,t_70,g_se,x_16)  
 目前对于c语言来说,迭代可以简单认为是循环结构。


那么我们如何用迭代的方式求斐波那契数呢?  
 【代码如下】



int Fib(int n)
{
int a = 1;
int b = 1;
int c = 1;
while (n>2)
{
c = a + b;//求出c的值
a = b;//a赋值给b,也就是a作为b的值
b = c;//b赋值给c,也就是b作为c的值
n–;
}
return c;
}

int main()
{
// 1 1 2 3 5 8 13 21 34 55,除前两位外,后一个数的值等于前两位相加

总结

上述知识点,囊括了目前互联网企业的主流应用技术以及能让你成为“香饽饽”的高级架构知识,每个笔记里面几乎都带有实战内容。

很多人担心学了容易忘,这里教你一个方法,那就是重复学习。

打个比方,假如你正在学习 spring 注解,突然发现了一个注解@Aspect,不知道干什么用的,你可能会去查看源码或者通过博客学习,花了半小时终于弄懂了,下次又看到@Aspect 了,你有点郁闷了,上次好像在哪哪哪学习,你快速打开网页花了五分钟又学会了。

从半小时和五分钟的对比中可以发现多学一次就离真正掌握知识又近了一步。

人的本性就是容易遗忘,只有不断加深印象、重复学习才能真正掌握,所以很多书我都是推荐大家多看几遍。哪有那么多天才,他只是比你多看了几遍书。

习,花了半小时终于弄懂了,下次又看到@Aspect 了,你有点郁闷了,上次好像在哪哪哪学习,你快速打开网页花了五分钟又学会了。

从半小时和五分钟的对比中可以发现多学一次就离真正掌握知识又近了一步。

[外链图片转存中…(img-sE5zeDn3-1714494808473)]

人的本性就是容易遗忘,只有不断加深印象、重复学习才能真正掌握,所以很多书我都是推荐大家多看几遍。哪有那么多天才,他只是比你多看了几遍书。

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

  • 12
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值