目录
- 引言
- 一.函数的递归
- 1.定义
- 2.持续递归
- 3.抽象解释
- 4.递归的核心要素
- 5.递归的使用(抽象解释)
- 6.递归的特点
- 二.递归与循环
引言
函数的递归不只是函数的套娃调用,更是把问题层层拆解,逐层还原的思维方式,是连接数学,算法与程序设计的桥梁。其意义在于以自相似方式拆解问题,它贴近数学归纳与分治,回溯,树遍历等算法,使代码简洁,结构清晰,同时提醒我们注意边界,调用栈与性能,是重要的思维方式。
一.函数的递归
1.定义
递归是编程的一种技术——一个函数在其定义内部调用自身(递归一定是依赖于函数的)
2.持续递归
一直调用会死递归(栈溢出),使程序崩掉
3.抽象解释
递归相当于“镜子中的镜子”或“俄罗斯套娃”,把一个大型的复杂问题,层层转化为一个与原问题相似但规模较小的子问题来求解,直到子问题不能再被拆分递归就结束了
4.递归的核心要素
(1)递归调用(递推阶段):
函数自己调用自己,每次调用时,问题都应该比上一次更小,逐步逼近一个最简单的“基础情况”
(2)终止条件(基础情况):
一个不再进行递归调用,能直接返回结果的特定条件,如果没有终止条件,递归会无限进行下去,最终导致栈溢出错误
5.递归的使用(抽象解释)
- 写递归可先写完成步骤(拆分),然后将其按步骤包装
- 先执行结束前的最后一步[若前无语句](不需要再拆分的那步及其对应的语句)
- 或将函数功能当成已经实现的,将其单独写明判断后,再实现要求的最后一步即可/含该函数的式子
- 需格外注意,递归要有条件,且每次递归都要更逼近条件
6.递归的特点
用少量的代码完成大量的运算(会占用较多内存)
(1)数值过大时反应时间长
在一些问题中(如计算斐波那列数列),在数值过大时,会因为反复计算子问题,导致时间复杂度指数级增加,最终占用过多时间,使程序性能堪忧
(2)栈溢出
- I.C语言中,每次调用函数都需要为本次函数在内存的栈区上,申请一块内存空间,来保存函数调用期间各种局部变量的值——这块空间叫做运行时堆栈或函数栈帧函数;如果不返回,函数表对应栈帧空间就会被一直占用,
- II.因此如果函数调用中存在递归调用时,每一次递归函数调用都会开辟属于自己的栈帧空间,直到函数递归不再继续,开始回归时,才会逐层释放栈帧空间(若采用函数递归的方式完成代码,递归层次过大或过深,就会浪费大量栈帧空间,也坑能引起栈溢出的问题)
- III.关于上述问题可用循环来解决
- IV.另一种优化递归程序中栈溢出的方式——尾递归(不一定可靠)
二.递归与循环
有时用递归只是因为他比非递归更清晰,但有时循环实现效率更高
- 递归:
- 一个问题非常复杂,难以用循环方式实现,此时递归实现的简洁性可以补偿它所带来的运行时开销(一般情况下,递归深度<100层,并且不会带来大量冗余计算时,可以大胆使用递归写法)
- 循环
- 当这个问题使用递归解决存在明显缺陷时,需要考虑循环方式
递归长会使用到:树/图遍历,分治算法,回溯算法,在数据结构中会大量使用