【C语言】函数的递归与迭代详解(包含斐波那契数求解)

目录:

1、函数递归

2、函数迭代

3、递归与迭代间的差异

4、斐波那契数求解

一、函数递归

在C语言中,递归就是自己调用自己,下面我们以最简单的递归程序为例:

上图程序只是为演示递归的形式,并非为解决一定问题,上图程序最终会因为栈溢出而导致最后出错,输出结果如下图:

1、递归的基本思想

其本质就是一个把大事化小的过程。即把一个复杂的问题转化为一个与原问题内容相似,但规模比原问题要小的子问题来求解,直到原问题不可再进行拆分。

递归的运算过程其实是两步:

第一步“递”,即为递推,也就是把复杂问题逐层拆解到不能拆分地步的过程。(再该过程中并未完成真正意义上的计算)

第二步“归”,即为回归,也就是把拆解后的每个问题的运算结果从最小的问题逐层回归到计算表达式中传递给上一层问题,得到最终结果。

2、递归的限制条件

(1)递归存在限制条件(即停止条件),当某次满足该限制条件时,递归便不再继续

(2)每次递归调用之后都会越来越接近这个限制条件

3、实际程序举例

(1)我们就先以 n 的阶乘为例

阶乘的概念:一个正整数的阶乘是所有小于或等于该数的正整数的积,且0的阶乘为1,n得阶乘写作  n!。

  n 的阶乘就是1~n个数字相乘,故  n!=  (n-1)!

比如:5!=  5*4*3*2*1

           4!=  4*3*2*1

所以:5!=  5*4!

上面的这个拆解过程就是递归中递推的过程,当我们的n值==0时,n的阶乘就为1。至于其余数的阶乘计算就可以通过公式计算。

条件设置的两个分支如下:

那么下面我们来编辑我们的程序,首先主函数部分设置如下:

在主函数内我们调用了自定义阶乘函数,故我们下面来对阶乘函数进行定义,内容如下:

输入输出结果:

那么下面我们来推演一下阶乘函数递归的过程,我们先上图:

如图所示,阶乘的过程是先将原式进行不断地递推拆分,直到满足限制条件(停止条件)时停止;

在完成递推拆分的过程之后,函数开始将上面每一个递推式进行逐层计算来完成每层的返回值,一直回归到最后一个回归式计算出结果后传递给主函数。

(2)按顺序打印一个整数的每一位

我们再以“按顺序打印一个整数的每一位”这个程序的编写来分析。

输入一个整数 n,按照顺序打印这个整数的每一位。

比如:

输入:666         输出: 6  6  6

输入:521         输出: 5  2  1

那么接下来我们就开始对这个程序内容进行分析编写,首先是主函数部分,如下图:

主函数内我们调用了 print_num 函数,那么接下来我们就需要对其进行定义。

如此我们就来思考一下,如何可以使整数的每一位单独打印出来,思路如下:

我们知道在 C 语言中,“/” 表示整除,那么我们利用整除就可以逐渐消掉最后一位,

比如:123 / 10 = 12------ 3   3这一位就消掉了,使下一次运算的数字变为12,经过下一次运算

12 /  10  =  1 ------2    2这一位也就消掉了,使得下一次运算的数字变为1,而此时我们已经得到了最后一位数字,故也就不再需要下一次运算。

上述我们拆解的过程就是函数递归中递推拆解的过程,同时通过分析我们发现,我们也得到了函数递归的限制条件 n <= 9也就是在 n 消位只剩一位数时不再进行下一次递推,开始回归。可是回归又该如何设置呢?

上面的计算我们只是实现了消位运算来得到下一次递归的运算数值,但我们并没将消掉的每一位打印出来,这时 “%”模运算(取余运算)就显得非常得心应手了,以此来实现回归。

比如:第一次回归:1--------打印在屏幕上;

           第二次回归:12  % 10  =  2---------打印在屏幕上;

           第三次回归:123  %  10   =   3---------打印在屏幕上。

如此实现顺序打印,综上述内容即为自定义函数的设置及递归的思路,具体程序如下:

为使各位对递归过程更加清楚,我们用流程图来使过程更加清晰:

二、函数迭代

函数迭代其实就是把函数递归拆开来,把递归的过程用循环等方式来表现出来,我们还是以举例一阶乘的那个例子为例:

第一个条件设置与Fact的第一种情况一致,只不过在递归中我们是又调用了多次Fact,而在迭代中我们利用循环实现了最终阶乘结果。

三、递归与迭代对比

从上面程序的对比中我们可以发现,递归最突出的优点是使用少量的代码,就能完成非常复杂的任务。但是递归的效率却存在一定的问题,这就与函数栈帧开创有关了。

在C语言中每调用一次函数,系统就会为本次调用的函数在栈区开创一块内存空间来存储相应的值,这一块空间被称作函数栈帧

函数只要不返回,那么开创的函数栈帧就会一直存在,就会占用大量的内存空间从而影响CPU的运行效率,甚者由于递归层次过深,开创的空间过多导致栈溢出

只有在函数递归不再继续,开始回归时,才会逐层释放栈帧空间(系统回收)

故与此相比迭代虽然程序复杂,但是效率较高,在处理某些问题上会比递归更好。比如现实中许多问题是用递归的方式来进行解释的,这只是因为它比非递归的形式更加清晰,但是这些问题利用迭代实现往往比递归的效率更高。(比如下面所述“斐波那契数列”)

补充点:1、当一个问题非常复杂时,难以使用迭代的方式实现时,此时递归的简洁性便可以补偿运行时所带来的开销。

2、递归若有不恰当的书写就可能会导致无法接受的结果,此时我们还是应放弃使用递归,使用迭代来解决问题。

程序举例:斐波那契数列求第n个斐波那契数

1  1  2  3  5  8  13  21--------此类数列为斐波那契数列,由上述数字我们可以得出以下公式:

公式如此呈现,我们自然就会与上面阶乘的形式相联系,从而使用递归来解决问题。既如此,我们不妨就先使用递归来试试看:

首先编写主函数部分:

调用了 Fib() 函数那么接下来我们对其进行定义:

运行结果也如上图所示。

为了使各位对函数递归庞大的运算量有更直观地了解,我们对第三个斐波那契数的调用此时进行统计。

如图·所示,在求第45个斐波那契数时第三个斐波那契数被调用了433494437次,数字再庞大些则会导致栈溢出

所以我们最好还是使用迭代来解决这个问题:

我们观察斐波那契数列1  1  2  3  5  8  13  21--------,可知前两个数都是1,前两个数相加就是第三个数

那么下面我们来编写程序,主函数部分基本不变,主要是自定义部分  Fib() 函数的内容改变:

我们将 c 初始化为 1 是为了保证输入的n为1时,斐波那契数打印值也为1。

对比递归的代码内容,我们可从迭代的内容知迭代的效率的确要比递归高一些,并且省内存。

故到底是用迭代还是递归还是要仔细思考后再进行选择!!!

写作不易,不知各位老板能否给个一键三连或是一个免费的赞呢(*^▽^*)(*^▽^*),这将是对我最大的肯定与支持!!!谢谢!!!(*^▽^*)(*^▽^*)

  • 42
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 21
    评论
评论 21
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值