函数的使用之递归调用

如何进行函数的递归调用? 递归是一种在函数内部调用自身的编程技巧。它将一个问题分解为一个或多个规模较小的相似子问题,每个子问题都可以通过调用相同的函数来解决。基本情况(Base Case):这是递归算法中的终止条件。在基本情况下,函数不再调用自身,而是直接返回一个结果。基本情况是确保递归不会无限循环的关键。递归情况(Recursive Case):这是递归函数继续调用自身的部分。在递归情况下,函数将问题分解为规模较小的子问题,并通过调用自身来解决这些子问题。 阅读详情

前言

在前面的几篇文章中,我们了解并使用了一些函数的基本用法,但函数除了能够被主函数调用以外,其实还能被自己或是其他的函数调用,而这种特殊的用法就是我们今天讲到的——函数的递归调用

1.递归调用的含义

递归是函数的一种特殊用法,其在C语言中的含义就是自己调用自己。(“递”:递进,“归”:回归)

其中最形象的例子就是:

现在我们就举一个例子:

#include<stdio.h>
int main()
{
printf("hehe\n");
main();//在main函数中又调用了main函数
return 0;
}
//由于在这串代码中没有限制条件,故会一直叠加调用main函数,无限重复的调用自己
//死循环,最后会崩溃(栈溢出)

由此,我们知道如果在递归调用函数的时候,不给施加限制条件那么就有可能出问题。

(栈溢出:即函数在实行的时候用的空间大于了内存分配给他的空间)

递归的思想

把一个比较复杂的问题,层层简化转化为一个与原问题相似但是计算量更小的问题,直到子问题无法被拆分,那么递归就结束了,其实递归就是“大事化小”的过程

2.递推的限制条件

刚才的那个代码由于没有写限制条件,所以一直“递",而没有”归“,而导致了栈溢出的问题。那么我们应该如何规避这一问题呢?——设置限制条件

限制条件包括两个方面:①在满足这个限制条件以后,递归就不再继续(必要条件,没有不行,有了也不一定成立)②每次归调用以后会越来越接近这个限制条件

3.关于递归调用的应用举例

3.1求n的阶乘(不考虑栈溢出的情况)

阶乘公式:n!=(n-1)!*n

思路:此处我们找到了n与n-1之间的递推关系,我们可以通过求(n-1)!来求n!,以此类推,我们求(n-1)!就可以通过求(n-2)!,一直细分下去,每次剥一个数字下来,最后在一次穿上即可。

背后的逻辑:由于递推公式的特性,前一项与后一项的关系与后一项与后一项之间的关系的形式相似,因此可以不断调用同一个结构相同的函数。通过求后一项的结果来”归“前一项的表达式当中从而得到前一项的值,把一个复杂的运算逐步细分,在把最后得到的结果依次向上带入。

//我们假设Fact()就是实现阶乘的函数,那么以上的思路就可以表达为:
Fact(n)=Fact(n-1)*n
//此处的Fact(n)就是n!,而Fact(n-1)就是(n-1)!
//每次使n-1,当n=1的时候就不在调用,那么这样就越来越接近限制条件了

注:由于在这里我们出现了n-1的情况,即n>=2,那么我们就分为n=1和n>=2的情况

即:Fact(n)=①1,n<=0②n*Fact(n-1),n>=2

注:我们这里的情况①并没用n=1,而是n<=0:n=1的时候仍会被调用,且是最后一次调用

那么最后的代码实现就是这样的

#include<stdio.h>
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("%d",ret);
return 0;
}

 我们可以通过这个图,对于递归调用的代码实现有更加深入的了解:先由地推表达式对于目标进行拆分,拆到可计算的部分,在每次算一点,依次”归“上去即可。

3.2 顺序打印一个整数的各个位数

描述:输入一个整数,从高位到低位,依次打印各位的数字。

eg:输入:1234,输出:1 2 3 4

那么如何做呢?

输入一个整数,计算机无法得知输入的整数有多少位,但如果输入的整数只有一位的时候,由num/10==0,即可判别只有一位,那么我们只用减少输入的整数的位数,直至仅剩一位的时候,即可计算了。

实现的逻辑分析:①打印出整型中的各位数字②把答应出的数字倒序输出

①如何打印出一个整数的个位数:

技巧1:如何求最后一位:num%10(num>=0)的结果即为个位数

技巧2:如何向后推进:(即如何摒弃个位)num/10

eg:print(1234)=4+print(123)(1234%10+1234/10,把4剥离出来,先层层剥离,最后再倒序依次打印)=4+(3+print(12))........,相似的结构,重复使用(把较大的数据,层层细分为可知的数据)

具体的代码是实现:

void Print(int n)
{
if(n>9)
{
  Print(n/10);//每次减少1位,再把结果作为形参,再次传给n,再继续。
}
  printf("%d",n%10);//直到只有一位时,才依次输出
/*else prinf("%d",n);//n<=9,即此时输入的整数只有一位,不用计算,本身即为其最后一位*/
//这一步其实不用,只有一位,其实位有多位,递归结束时的那种情况
}
#include<stdio.h>
int main()
{
int m=0;
scanf("%d",&m);
Print(m);
return 0;
}

当输入的数不是个位数的时候,会进入if当中,不断的递归,只有当不在满足n>9的条件时才跳出,开始执行递归调用以外的命令。

注:递归调用只会在每次的函数调用完成后,才会返回这一层的结果。先把里层的函数执行完成后再逐渐向后扩展

4.慎用函数的递归

程序在运行的时候会在栈区开辟一块空间来加载数据(运行时的堆栈或函数栈帧),局部变量就存储在栈区当中,当程序运行出某个局部变量所在的”{}“时,其在栈区中开辟的空间就被取消了。

而对于递归调用也类似,递归在”递(递推)“的过程中,函数一直未返回,为函数分配的栈帧空间就一直被占用,每深入一层都会为自己开辟一块栈帧空间。

直至”递“结束,在”归“的时候在逐层取消其开辟的栈帧空间(回收),那么如果递归的次数越多,那么占用的空间就越多。

如果函数的层次太深,就会一直开辟空间,而栈区的空间又有限,就会导致栈溢出。

用递归来解决问题,虽然用的代码较少,但容易栈溢出,当递归的次数比较多的时候,我们通常用迭代(循环)来代替。

并且使用迭代的效率往往高于函数递归,而当一个问题比较复杂,难以用迭代实现时,此时递归实现的简洁性便可以补偿它所带来运行时的开销。

下面我们就以兔子数列(斐波那契数列)为例,

f1=1,f2=1,fn=fn-1+fn-2(这恰是一个递归表达式)由此我们用递归能轻易的实现,代码如下:

int f(int n)
{
if(n<=2)
renturn 1;
else return f(n-1)+f(n-2);
}
#include<stdio.h>
int main()
{
int n=0;
scanf("%d",&n);
int ret=f(n);
printf("%d\n",ret);
return 0;
}

此时,对于一个比较大的数,就会花费计算机比较长的时间去计算,这不利于我们平时的效率。但这是为什么呢?

其实在这次的运算中,存在很多次重复的计算,

f(50)=f(49)+f(48),而f(49) f(48)为未知,有分别拆分为f(48)+f(47)和f(46)+f(47),由此,按此规律,①计算f(50)就要计算2^49次,太慢。②从第二次计算我们就能看出,在求f(49) f(48)时均包含了f(47)那么f(47)即其后的内容就多算了一次,等等。所以在递归调用当中存在有许多冗余的计算。

下面我们通过一个具体的例子来看一下,使用递归在其层次比较深时,计算的重复度有多高。

#include<stdio.h>
int count=0
int f(int n)
{
if(n==5)
count++
if(n<=2)//前两项不用计算,直接为1,1
return 1;
else
f(n)=f(n-1)+f(n-2);
}
int main()
{
int n=0;
scanf("%d",&n);
int ret=f(n);
printf("%d",count);
return 0;
}

然而当我用迭代(循环)来实现的时候,就会快的多。

思维逻辑:1,1,2,3,5........

                   a    b

①斐波那契数列的第n项,即为前两项的和。
②当一共只有3项的时候,我们计算第三项的方法是把前两项加起来即得到第三项。
③而我们现在想要得到第n项,只用把a,b依次向后移动即可

那么我们怎么让a,b顺次移动呢?————中间量法

我们在a,b以外再定义一个变量,用于保存他们的和,作为新的b,再把b作为新的a即可。

即:

int f(int n);
int a=1;
int b=1;
int c=1;
while(n>2)
{
c=a+b;
a=b;
b=c;
n--;
}
return 0;
return c;
}

此时我们就只用计算n-2项,大大提升了效率

关于何时使用递归,又何时使用迭代的几点建议:

①如果用递归能简单方便的写出代码,而有没有什么明显的缺陷,且运行正常时使用

 ②如果再用递归时,存在明显的缺陷,如:栈溢出、效率低下,此时务必更换为其他方法 

5.两个经典的函数递归问题

经典的汉诺塔问题,青蛙跳台阶问题,都是函数的递归的典型应用而展开的。

青蛙跳台阶问题:一共有n阶台阶,青蛙一次能跳1或2阶台阶,问一共有多少种跳法?

其实就可以看为f(1)=1,f(2)=2的斐波那契数列,当我们求第n项时,我们就想,要能跳到这第n阶,那么要么从倒数第二节开跳,要么从倒数第一节开始,即:f(n)=f(n-1)+f(n-2),而f(n-1)与f(n-2)又可以继续细分,然后就是一个标准的求斐波那契第n项(有n阶时的总方法数)的问题了。

汉诺塔问题:把A杆上的金盘全部移到C杆上,并仍保持原有顺序叠好。操作规则:每次只能移动一个盘子,并且在移动过程中三根杆上都始终保持大盘在下,小盘在上,操作过程中盘子可以置于A、B、C任一杆上。 

①A上只有一个的时候,直接移到C

②当有两个及以上的时候,把A柱子上的盘子分为两部分,上面(n-1)个,和最后一个,先把上方的n-1个放在B柱子上,最后一个放在C上,再把B的放到A上再执行相同的操作。

即Hanio(n)=Haino(n-1)+Hanio(1) 

后言

当然作为算法的最经典模型之一的斐波那契数列,求其第n项的方法还有很多,如:矩阵快速幂等等,我们将会再后面的文章中提到。

创作不易,求各位看官,给我你们手中免费的点赞,收藏和关注吧❤

java方法递归调用【详细解释(解决什么问题、递归执行机制等)】 递归就是方法自己调用自己,每次调用时传入不同的变量。 递归有助于编程者解决复杂问题,同时可以让代码变得简洁 阅读详情

相关推荐

函数递归调用详解

递归问题是一个说简单也简单,说难也有点难理解的问题.我想非常有必要对其做一个总结. 首先理解一下递归的定义,递归就是直接或间接的调用自身.而至于什么时候要用到递归,递归和非递归又有那些区别?又是一个不太容易掌握的问题,更难的是对于递归调用的理解.下面我们就从程序+图形的角度对递归做一个全面的阐述. 我们从常见到的递归问题开始: 1 阶乘函数 #include using names

Jason 5083

方法递归调用(非常非常重要,比较难)

今天,我们来学习一下方法的递归,希望我的讲解可以让您对方法的递归调用有更深的了解。递归调用就是在当前的函数中调用当前的函数并传给相应的参数,这是一个动作,这一动作是层层进行的,直到满足一般情况的的时候,才停止递归调用,开始从最后一个递归调用返回。简单的说: 递归就是方法自己调用自己,每次调用时传入不同的变量.递归有助于编程者解决复杂问题,同时可以让代码变 得简洁。2、各种算法中也会使用到递归,比如快排,归并排序,二分查找,分治算法等。3、将用栈解决的问题,代码实现变得更加简洁。.........

qq_64513441的博客 3566

函数的递归调用

什么是递归 函数调用是通过栈实现的,在调用函数时,系统会将被调用函数所需要的程序空间安排在一个栈中,每当调用一个函数时,就在栈顶为他分配一个存储区,每当一个函数退出时就释放他的存储区,也就是说,当前正在运行的函数的存储区是在栈顶的。因为栈是先进后出的,所以当有多个函数嵌套调用时,会按照先调用后返回的原则进行运行。 递归也是一种函数调用,只不过是函数自己调用自己,是一种特殊的函数调用。这时有说,函数...

人生代码 ---- 公众号 1702

递归调用

递归(recursion):一个函数在其定义中直接或间接调用自身的一种方法。 数学公式表示关系,数学归纳法。 回推和递推,递归结束(终止)条件 数学归纳法公式: 1 n=0,1; n! = n*(n-1)! n>1; int fun(int n) //递归:复杂问题好理解 { int c = 0; if((1 == n)||(0 == n)) { c = 1; } else ...

jiechuhoudeshang的博客 2313

MS1030超声波流量计电路实战:如何用15ps精度优化水表设计(附配置代码)

本文深入探讨了如何利用MS1030超声波流量计芯片实现15ps高精度测量,以优化水表设计。文章从系统误差分配出发,详细解析了硬件电路设计要点、SPI通信配置及核心优化技巧,如利用可编程偏置电压和8回波累加功能来提升抗干扰能力与测量稳定性,并附有实用的配置代码示例。

svm4gardener的博客 335

全国各省份影像下载地址(11级别)

cesium中使用urltemplate加载,leaflet中使用tile加载,具体加载有问题可以给我留言。

资深三维GIS开发 5152

python函数的递归调用

函数递归就是函数的递归调用,是函数嵌套调用的一种特殊形式,具体就是指在调用一个函数的过程中直接或者间接的调用到本身,递归的本质就是循环做重复的事情。在调用func的过程中又调用func,这就是直接调用函数本身;在调用func的过程中调用foo,而在调用foo的过程中又调用func,这就是间接调用func本身。通过上面的分析,两种情况下的函数递归调用都是一个无限循环的过程,Python为了防止函数递归进入无限循环对函数递归调用的深度做了限制,一旦超出限制就会抛出异常。

weixin_71807218的博客 2070

C# 开发 WPS 插件实战:从环境搭建到功能实现(附完整代码)

本文提供了一份详尽的C#开发WPS插件实战指南,涵盖从环境搭建到功能实现的完整流程。通过COM技术,开发者可以创建自定义插件,为WPS Office扩展自动化功能,如文档处理与格式转换。文章包含核心概念解析、Ribbon界面集成、调试部署方法及完整代码示例,帮助开发者快速上手。

rust6ferris的博客 928

函数的嵌套调用和递归调用

1.嵌套调用 如果函数1调用了函数2,函数2再调用了函数3,便形成了函数的嵌套调用。 例如: #include &amp;lt;iostream&amp;gt; using namespace std; int fun2(int m) { return m*m; } int fun1(int x,int y) { return fun2(x)+fun2(y); } int main(){ ...

elinice 7164

四足机器人机械结构设计:从腿型到驱动的全面解析

本文全面解析了四足机器人机械结构设计的核心要点。资深工程师老张深入探讨了并联腿与串联腿的选型策略、不同腿型布局对稳定性的影响,并详细对比了皮带轮与连杆驱动方式的优劣。文章结合实战经验,强调了足端设计、材料选择及整体结构轻量化等关键细节,为机器人开发者提供了从理论到实践的宝贵指导。

assembly8low的博客 633

毕设分享|基于16单片机的指纹识别电子密码锁设计

我们今天的工作生活中,很多的场合都需要身份的认证,而传统的基于标志的身份认证技术由于受到证件伪造以及密码破解等手段的威胁,逐渐表现得有些力不从心。怎样保护自己的重要文件资料,如何保证自己的隐私不被泄露?在传统的身份认证中,我们往往使用密码加密法,但是这种方法只是"防君子不防小人"。在高明的黑客眼里,由几个字符组成的密码脆弱得不堪一击。现在,科技的发展让我们有了新的选择——生物识别技术。将生物识别技术应用于笔记本、门锁等方面,可以对文件、财产起保护作用,并且可以进行身份识别。

Sen_091的博客 1021

函数递归调用

我们学习了函数的嵌套调用,可以在函数中调用函数。那么,如果在一个函数中,调用自己这个函数,那么,这个执行过程称为:函数递归调用。这个函数也称为:递归函数。程序测试例子: 程序运行结果如下: 在这个测试例子中,我们定义了func函数,在func函数中又调用了func函数自己;所以,这个过程称为:递归调用。 我们知道,在程序的执行过程中,碰到函数调用的时候,就跳转到函数定义的函数体中执行,那么,函数的递归调用。就是函数又跳转到自己定义的函数体中运行;那么,我们分析上面例子中,函数的递归..

LC的专栏 1524

python cartopy绘制扇形区域图/cartopy绘制北极部分区域

实际上,这个问题原因还是由于投影转换的问题,在set_extend时,绘制的上下边界仍然是方形、未被正确投影的边界,与我们的set_boundary存在冲突,最根本的原因还是在于cartopy对于投影计算的一些缺陷。另外我们需要指出的是:**该方法不适用于极地投影,即NorthPolarStereo,由于NorthPolarStereo本身投影特性只需一个参数,本身并不适合。我们绘制极地投影时,同样也是使用set_boundary绘制圆形边界,那么当我们想要绘制扇形时,可以通过。请根据喜好自行选择,

weixin_43750300的博客 3212

函数的嵌套调用与递归调用

函数的嵌套调用与递归调用 实现C语言求a的n次方

ChengYuanCY的博客 4589

【AI理论学习】深入理解扩散模型:Diffusion Models(DDPM)(理论篇)

继OpenAI在2021提出的文本转图像模型之后,越来越多的大公司卷入这个方向,例如谷歌相继推出了和。一些主流的文本转图像模型,例如,和采用了扩散模型()作为图像生成模型,这也引发了对扩散模型的研究热潮。与GAN相比,扩散模型训练更稳定,而且能够生成更多样的样本,OpenAI的论文也证明了

小哲的博客 5213

理想L9高通QAM8797座舱域控制器全拆解|全网独家复现 硬件架构升级、电源散热优化、多屏低延迟交互、车机系统赋能高端智能座舱落地

随着智能汽车向舱驾一体、全域智能化、多模交互方向迭代,传统单一功能座舱芯片已无法满足多屏高刷渲染、全车AI语音、车内视觉感知、高速车载通信的叠加算力需求。2026款全新理想L9作为旗舰级智能SUV,全面搭载高通新一代旗舰座舱域控制器QAM8797,搭配自研马赫M100 AI芯片、国产车规主控芯片,构建起高性能、高可靠、低延迟的车载中央计算平台。

getusushu的博客 382

Python函数的递归调用

一:递归的定义 函数的递归调用:是函数嵌套调用的一种特殊形式 具体是指:在调用一个函数的过程当中,又直接或者间接地调用到了本身 Python对递归调用循环调用的次数有限制,为1000次 1.调用的例子: 直接调用本身 def f1(): print('是我') f1() f1() 间接调用本身 def f1(): print('我是f1') f2() def f2(): print('我是f12') f1() f1() 2.一段代码的循环运行方案有2

python学习者的博客 6185

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

函数调用,递归

return 0;这个里面:main ---成为调用者 ---main函数是整个程序的入口,只能调用者getMonthDays --- 在此处是 被调用者} //函数的嵌套调用注:函数不支持 嵌套定义,但是可以嵌套调用函数名 --- 函数的入口地址函数调用的本质:实际是利用的栈的结构 ---先进后出 --保证了函数可以层层嵌套调用1.栈的使用:C语言使用栈(stack)来管理函数调用过程中的数据和控制信息。

Zby1586的博客 1553

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

八、函数的递归调用

一、 什么是函数递归调用 函数的递归调用是函数嵌套调用的一种特殊形式,在调用一个函数的过程中又直接或者间接地调用该函数 本身,称之为函数的递归调用 递归调用必须有两个明确的阶段: 1. 回溯: 一次次递归调用下去,说白了就一个重复的过程,但需要注意的是每一次重复问题的规模都应该有所减少,直到逼近一个最终的结果,即回溯阶段一定要有一个明确的结束条...

weixin_34292924的博客 982

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

上一篇: 数组与函数的综合运用—扫雷游戏
下一篇: 万恶之源----指针<第一弹>
小比特newer
博客等级 码龄3年 185粉丝 21原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小比特newer

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值