“手把手教你玩转函数递归,建议收藏!“

目录

1. 什么是递归

2. 递归的限制条件

3. 递归的举例

4. 递归与迭代


正⽂开始

1. 递归是什么?

递归是学习C语⾔函数绕不开的⼀个话题,那什么是递归呢?

递归其实是⼀种解决问题的⽅法,在C语⾔中,递归就是函数⾃⼰调⽤⾃⼰

写⼀个史上最简单的C语⾔递归代码:

上述就是⼀个简单的递归程序,只不过上⾯的递归只是为了演⽰递归的基本形式,不是为了解决问 题,代码最终也会陷⼊死递归(死循环),导致栈溢出

递归的思想:

把⼀个⼤型复杂问题层层转化为⼀个与原问题相似,但规模较⼩的⼦问题来求解;直到⼦问题不能再 被拆分,递归就结束了。所以递归的思考⽅式就是把⼤事化⼩的过程。

递归中的递就是递推的意思,归就是回归的意思,接下来慢慢来体会。

2. 递归的限制条件

递归在书写的时候,有2个必要条件:

• 递归存在限制条件,当满⾜这个限制条件的时候,递归便不再继续。

• 每次递归调⽤之后越来越接近这个限制条件。

在下⾯的例⼦中,我们逐步体会这2个限制条件。

3. 递归举例

3.1 举例1:

求n的阶乘 计算n的阶乘(不考虑溢出),n的阶乘就是1~n的数字累积相乘。

3.1.1 分析和代码实现

我们知道n的阶乘的公式: n ! = n∗(n−1)!

这样的思路就是把⼀个较⼤的问题,转换为⼀个与原问题相似,但规模较⼩的问题来求解的。

n!---> n*(n-1)!

(n-1)!---> (n-1)*(n-2)!

……

直到n是1或者0时,不再拆解

再稍微分析⼀下,当 n<=1 的时候,n的阶乘是1,其余n的阶乘都是可以通过上述公式计算。 n的阶乘的递归公式如下:

那我们就可以写出函数Fact求n的阶乘,假设Fact(n)就是求n的阶乘,那么Fact(n-1)就是求n-1的阶 乘,函数如下:

测试:

递归在函数里面的演变过程:

3.2 举例2:

顺序打印⼀个整数的每⼀位 输⼊⼀个整数m,打印这个按照顺序打印整数的每⼀位。

⽐如:

3.2.1 分析和代码实现

这个题⽬,放在我们⾯前,⾸先想到的是,怎么得到这个数的每⼀位呢?

如果n是⼀位数,n的每⼀位就是n⾃⼰

n是超过1位数的话,就得拆分每⼀位

但是我们有了灵感,我们发现其实⼀个数字的最低位是最容易得到的,通过%10就能得到

那我们假设想写⼀个函数Print来打印n的每⼀位(注意这里面的Print表示一个定义函数,不表示任何意义),如下表⽰:

直到被打印的数字变成⼀位数的时候,就不需要再拆分,递归结束。

那么代码完成也就⽐较清楚:

在这个解题的过程中,我们就是使⽤了⼤事化⼩的思路

把Print(1234) 打印1234每⼀位,拆解为⾸先Print(123)打印123的每⼀位,再打印得到的4

把Print(123) 打印123每⼀位,拆解为⾸先Print(12)打印12的每⼀位,再打印得到的3

直到Print打印的是⼀位数,直接打印就⾏。

3.2.2 画图推演

以1234每⼀位的打印来推演⼀下

4. 递归与迭代

递归是⼀种很好的编程技巧,但是很多技巧⼀样,也是可能被误⽤的,就像举例1⼀样,看到推导的公 式,很容易就被写成递归的形式:

Fact函数是可以产⽣正确的结果,但是在递归函数调⽤的过程中涉及⼀些运⾏时的开销。

在C语⾔中每⼀次函数调⽤,都要需要为本次函数调⽤在栈区申请⼀块内存空间来保存函数调⽤期间 的各种局部变量的值,这块空间被称为运⾏时堆栈,或者函数栈帧。

函数不返回,函数对应的栈帧空间就⼀直占⽤,所以如果函数调⽤中存在递归调⽤的话,每⼀次递归 函数调⽤都会开辟属于⾃⼰的栈帧空间,直到函数递归不再继续,开始回归,才逐层释放栈帧空间。

所以如果采⽤函数递归的⽅式完成代码,递归层次太深,就会浪费太多的栈帧空间,也可能引起栈溢 出(stackoverflow)的问题。

所以如果不想使⽤递归就得想其他的办法,通常就是迭代的⽅式(通常就是循环的⽅式)。

⽐如:计算n的阶乘,也是可以产⽣1~n的数字累计乘在⼀起的。

上述代码是能够完成任务,并且效率是⽐递归的⽅式更好的。

事实上,我们看到的许多问题是以递归的形式进⾏解释的,这只是因为它⽐⾮递归的形式更加清晰, 但是这些问题的迭代实现往往⽐递归实现效率更⾼。

当⼀个问题⾮常复杂,难以使⽤迭代的⽅式实现时,此时递归实现的简洁性便可以补偿它所带来的运 ⾏时开销。

举例3:求第n个斐波那契数 我们也能举出更加极端的例⼦,就像计算第n个斐波那契数,是不适合使⽤递归求解的,但是斐波那契 数的问题通过是使⽤递归的形式描述的,如下:

看到这公式,很容易诱导我们将代码写成递归的形式,如下所⽰:

当我们n输⼊为50的时候,需要很⻓时间才能算出结果,这个计算所花费的时间,是我们很难接受的, 这也说明递归的写法是⾮常低效的,那是为什么呢?

其实递归程序会不断的展开,在展开的过程中,我们很容易就能发现,在递归的过程中会有重复计 算,⽽且递归层次越深,冗余计算就会越多。我们可以作业测试:

这⾥我们看到了,在计算第40个斐波那契数的时候,使⽤递归⽅式,第3个斐波那契数就被重复计算了 39088169次,这些计算是⾮常冗余的。所以斐波那契数的计算,使⽤递归是⾮常不明智的,我们就得 想迭代的⽅式解决。

我们知道斐波那契数的前2个数都1,然后前2个数相加就是第3个数,那么我们从前往后,从⼩到⼤计 算就⾏了。

这样就有下⾯的代码:

有时候,递归虽好,但是也会引⼊⼀些问题,所以我们⼀定不要迷恋递归,适可⽽⽌就好。


                                                                                                                                                                                                                                                                                                      完

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/460518.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

阿里云服务器多少钱?2024年阿里云服务器租用费用表大全

2024年阿里云服务器租用价格表更新&#xff0c;云服务器ECS经济型e实例2核2G、3M固定带宽99元一年、ECS u1实例2核4G、5M固定带宽、80G ESSD Entry盘优惠价格199元一年&#xff0c;轻量应用服务器2核2G3M带宽轻量服务器一年61元、2核4G4M带宽轻量服务器一年165元12个月、2核4G服…

免费生成ios证书的方法(无需mac电脑)

使用hbuilderx的uniapp框架开发移动端程序很方便&#xff0c;可以很方便地开发出移动端的小程序和app。但是打包ios版本的app的时候却很麻烦&#xff0c;官方提供的教程需要使用mac电脑来生成证书&#xff0c;但是mac电脑却不便宜&#xff0c;一般的型号都差不多上万。 因此&a…

java_error_in_pycharm.hprof文件是什么?能删除吗?

java_error_in_pycharm.hprof文件是什么&#xff1f;能删除吗&#xff1f; &#x1f335;文章目录&#x1f335; &#x1f333;引言&#x1f333;&#x1f333;hprof格式文件介绍&#x1f333;&#x1f333;java_error_in_pycharm.hprof文件什么情况下能删除&#x1f333;&…

C#静态数组删除数组元素不改变数组长度 vs 动态数组删除数组元素改变数组长度

目录 一、使用的方法 1.对静态数组删除指定长度并不改变数长度的方法 &#xff08;1&#xff09;静态数组 &#xff08;2&#xff09;对静态数组删除元素不得改变其长度 2.对动态数组删除指定长度并改变数长度的方法 &#xff08;1&#xff09;动态数组 &#xff08;2&a…

故障诊断 | 一文解决,TCN时间卷积神经网络模型的故障诊断(Matlab)

效果一览 文章概述 故障诊断 | 一文解决,TCN时间卷积神经网络模型的故障诊断(Matlab) 模型描述 时间卷积神经网络(TCN)是一种用于序列数据建模和预测的深度学习模型。它通过卷积操作在时间维度上对序列数据进行特征提取,并且可以处理可变长度的输入序列。 要使用TCN进行…

L1-080 乘法口诀数列

一、题目 二、解题思路 三、代码 #include<iostream> using namespace std; int main() {int a1,a2,n;cin>>a1>>a2>>n;if(n1){cout<<a1;return 0; }int a[n*2];cout<<a1<<" "<<a2;a[0]a1;a[1]a2;for(int i2,j2;i&l…

05 06 Verilog基础语法与应用讲解

05. 1. 位操作 计数器实验升级&#xff0c;设计8个LED灯以每个0.5s的速率循环闪烁&#xff08;跑马灯&#xff09; 1.1 方法1&#xff1a;使用移位操作符<<来控制led灯的循环亮灭 设计代码 Verilog中&#xff0c;判断操作的时候不加位宽限定是可以的&#xff0c;比如i…

Qt QVariant类应用

QVariant类 QVariant类本质为C联合(Union)数据类型&#xff0c;它可以保存很多Qt类型的值&#xff0c;包括 QBrush&#xff0c;QColor&#xff0c;QString等等&#xff0c;也能存放Qt的容器类型的值。 QVariant::StringList 是 Qt 定义的一个 QVariant::type 枚举类型的变量&…

二分算法--模板及原理总结

二分答案 首先我们看这个图&#xff1a; 我们需要二分的答案就是这个临界点x。 什么情况下可以使用二分呢&#xff1a; 具有单调性&#xff08;单调递增&#xff0c;单调递减&#xff09;&#xff0c;二段性&#xff08;整个区间一分为二&#xff0c;一段区间满足&#xff0c;一…

git flow与分支管理

git flow与分支管理 一、git flow是什么二、分支管理1、主分支Master2、开发分支Develop3、临时性分支功能分支预发布分支修补bug分支 三、分支管理最佳实践1、分支名义规划2、环境与分支3、分支图 四、git flow缺点 一、git flow是什么 Git 作为一个源码管理系统&#xff0c;…

Apktool任意文件写入漏洞分析 CVE-2024-21633

前置知识 在复现该漏洞前&#xff0c;有必要了解Apktool和resources.arsc相关的基础知识&#xff0c;方便理解后续POC的构造。 Apktool是一款流行的开源逆向工程软件&#xff0c;用于反编译和编译Android应用&#xff0c;因此&#xff0c;Apktool被许多其他逆向工程软件集成。…

时间序列之周期性

什么是序列相关&#xff1f; 针对时间序列的趋势和季节性&#xff0c;我们可以很容易地利用“时间相关”的属性进行建模&#xff0c;即直接从时间索引中得出特征。但是有些情况下&#xff0c;一些时间序列只能利用“序列相关”属性&#xff0c;即使用序列的历史值作为特征。如…