函数递归

哈喽啊各位,真是,好久好久好久不见。这段时间实在是太过忙碌了昂,还望诸君见谅,接下来时间会松很多,咱们也会恢复正常更新速度啦

小希在这里祝诸君:期末不挂科,四六级都过!功不唐捐,玉汝于成!!!

今天昂,咱们来认识认识函数递归

一 . 什么是递归?

在我们的C语言中,“ 递归 ”是我们相当重要的一环,简而言之,递归就是——函数自己调用自己。这是一种高效解决问题的办法。

这里为诸君演示一个最简单的递归函数:

在main函数中调用main函数:

因为我们没有终止信息,程序就会一直调用,一直打印

这里代码最终会陷入死递归,导致栈溢出(Stack overflow)

在这里只是为了给诸君演示一下递归的基本形式,不是为了解决任何实际问题

二 . 递归的本质

在递归中:“ 递 ” 就是 “ 递推 ”,“ 归 ” 就是 “ 回归 ”

递归的本质就是,把一个大型复杂的问题层层转化为一个与原问题相似,但规模较小的子问题来求解,一直拆分到子问题不能再被拆分,递归就结束了。所以递归的思考方式就是将大事化小的过程

三 . 递归的限制条件

递归在书写的时候,有两个必要条件:  
(1) 递归存在限制条件,当满足这个限制条件的时候,递归便不再继续 
(2) 每次递归调用之后越来越接近这个限制条件

 四 . 递归举例

(1)计算 n 的阶乘 n!(不考虑溢出)

注意:一个正整数的阶乘(factorial)就是所有小于等于该数的正整数的乘积,且规定0的阶乘为1

因为我们知道,n 的阶乘公式为:n!= n * (n - 1)!,如:

又因为 0 的阶乘为 1 ,则当 n == 0 时,n 的阶乘为 1 

由此我们可以得出计算时的两种情况:

那么接下来我们就可以来实现这个阶乘函数:

画图演推:

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

例如:输入1234,打印1 2 3 4 

分析:如果n是一位数,n的每一位就是n自己,n是超过1位数的话,就得拆分每一位

1234%10就能得到4,然后1234/10得到123 ,这就相当于去掉了4 ,然后继续对123%10,就得到了3,再除10去掉3,以此 类推不断的 %10 和 /10 操作 ,直到1234的每⼀位都得到
 

但是这样我们就会发现一个问题:我们先得到的总是最低位的数,但题目要求我们需要顺位打印,这个时候我们就可以通过递归实现,先将它 / 10,直到 / 到最前方的一位,再打印,如:

画图推演:

五 . 递归和迭代

递归是一种很好很方便的编程技巧,但是我们需要将其运用在合适的环境下,并不是每一个环境中我们使用递归都会使代码更加简便,搞不好就会弄巧成拙

就如同我们举例的第一道题目,求一个数的阶乘。还记得我们在第一个题目的后面还特别注释了:不考虑栈溢出的问题,这是因为,我们运用递归的确是可以实现求阶乘的运算,但是我们在递归的调用时,会涉及到一些运用时的开销

在C语言中每一次函数调用,都需要为本次函数调用在内存的栈区,申请一块内存空间来保存函数调 调用 期间的各种局部变量的值,这块空间被称为 运行时堆栈,或者函数栈帧
函数不返回,函数对应的栈帧空间就一直占用,所以如果 函数调用中存在递归调用的话,每一次递归函数调用都会开辟属于自己的栈帧空间,直到函数递归不再继续,开始回归 ,才逐层释放栈帧空间 所以如果采用函数递归的方式完成代码,递归层次太深,就会浪费太多的栈帧空间,也可能引起栈溢 出(stack overflow)的问题   
 

所以在这个题型下,我们最优解应该是采用迭代的方式,也就是我们常说的循环,如:

在这个题型中,我们使用迭代的方式的效率是要高于使用递归的

由此可见,我们递归虽好,但不要滥用哦,只有当一个问题非常复杂,我们难以使用迭代的方式解决问题的时候,此时使用递归的简洁性便可弥补其使用带来的运行时的开销

六 . 斐波那契数

在这里我再来举一个例子来帮助诸君理解递归与迭代

斐波那契数列是什么呢?我这里就直接为诸君搬运百度百科了昂:

简而言之呢,就是这个斐波那契数列,前两项都为 1 ,此后的每一项都等于前两项之和

求第 n 个斐波那契数:

(1)运用递归方式

斐波那契数的运算原理如下,我们可以发现,这不就是我们的递归嘛

当我们求第 n 个斐波那契数的时候,它是不适合用递归的方式来求解的,这个时候就有小伙伴要真诚地发出疑问了:这是为什么呢?明明斐波那契数本身就是通过递归的方式来运算的,我们现在用递归的方式来实现它反而不合适了呢?Good question!!!

诸君不必心急,接下来咱们就先用递归的方式来实现它,我们看看会遇到哪些问题就明白了,毕竟实践才是检验真理的唯一标准嘛

此时我们可以看到,运用递归求求第 n 个斐波那契数没有任何问题,但当我们需要运算的数字非常大时,比如50,这个效率就非常慢昂:

反正我是等了大概3分多钟,有闲心的可以自己等一等哈哈,而且还得出了一个负数,这里应该就是出现了栈溢出的问题昂

通过递归计算效率极其低下的原因是:在我们递归的运行中,会有相当大量的重复计算,且递归的层次越深,冗余计算就会越多

如图,我们要通过递归计算第50个斐波那契数,这是一个倒序的运算过程,我们先要知道第49位的数和第48位数,我们要知道这两位数我们又得先知道第48,47,47,46位,以此类推......

由此可见,我们通过递归来实现它是还会有这非常大量的冗余计算的,所以效率极其低下

(2)运用迭代方式

当我们运用迭代方式来计算效率就高得多,无需多言,咱们直接上图昂:

运用了迭代的方式,没有了冗余的计算,咱们的运算效率就高得多昂,即使是50也是秒出答案

这就是递归和迭代的区别,咱们不能滥用,得根据题目所给出的环境选择合适的方法!

OKK,有关函数递归方面的知识今天咱们就谈到这里了,许久不见,甚是想念,还望各位继续与我一同前行,话不多说,咱们下期再见,与诸君共勉!!!

相关推荐

  1. Python:函数

    2024-06-17 15:54:01       22 阅读
  2. 函数(C语言)

    2024-06-17 15:54:01       38 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-06-17 15:54:01       18 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-06-17 15:54:01       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-06-17 15:54:01       18 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-06-17 15:54:01       20 阅读

热门阅读

  1. oracle跨版本exdp 从高版本到低版本 从18c到10g

    2024-06-17 15:54:01       8 阅读
  2. 小抄 20240611

    2024-06-17 15:54:01       5 阅读
  3. web前端网页实例:深度剖析与实践指南

    2024-06-17 15:54:01       8 阅读
  4. 【学习Shell脚本编程】

    2024-06-17 15:54:01       7 阅读
  5. 使用PLM系统对电池企业有哪些好处

    2024-06-17 15:54:01       5 阅读
  6. PostgreSQL的系统视图pg_auth_members

    2024-06-17 15:54:01       8 阅读
  7. centos 7 虚拟化单通道光纤 udev 的配置

    2024-06-17 15:54:01       8 阅读
  8. Linux/Ubuntu/Debian 常用用户管理命令

    2024-06-17 15:54:01       11 阅读
  9. 处理导入Excel文件过大导致Zip bomb detected的问题

    2024-06-17 15:54:01       8 阅读
  10. 如何在Unix系统上安装Perl?

    2024-06-17 15:54:01       8 阅读
  11. 一次关于JVM的面试经历,安卓开发快速学习

    2024-06-17 15:54:01       8 阅读
  12. Web前端编辑器:探索其深度与广度

    2024-06-17 15:54:01       7 阅读
  13. 【Python】在 Pandas 中使用 AdaBoost 进行分类

    2024-06-17 15:54:01       10 阅读