递归的本质

特别声明,以下大部分内容摘自李煜东的《算法竞赛进阶指南》

   理解递归,首先要从函数调用说起。实际上,一台32台的计算机采用“堆栈结构”来实现函数调用,它在汇编语言中,按照函数参数从右至左的顺序依次入栈(大部分编译器貌似都是从右至左),然后指向call(Address)指令。该指令把返回地址(当前语句的下一条语句)入栈,然后跳转到address位置的语句。在函数返回时,它指行ret指令。该指令会把返回地址出栈,并跳转到该地址执行。

   对于C++中函数定义非static的局部变量,每次执行call与ret指令时,会在栈中相应的保存与复原,而作用范围超过该函数的变量,以及动态申请的堆区空间(使用new关键字或者malloc函数且注意此处的堆并非数据结构中的堆)。栈指针、返回值、局部的运算会借助CPU的寄存器完成。为什么此处强调的是非static变量,因为C++中的所有static变量都是保存在内存四区中的静态区。全局static变量和局部static变量的唯一区别就在于二者的作用域不用以及初始化时机不同,具体初始化时机详见百度,此处不再赘述,但是二者的生命周期都和main函数是一致的。

  函数调用栈描述的是函数之间的调用关系。它由多个栈帧组成,每个栈帧对应着一个还未运行完的函数。栈帧中保存了该函数的返回地址和局部变量,因而不仅能在函数调用完毕之后找到正确的返回地址,还能保证不同函数之间的局部变量互相不会干扰--因为不同函数对应着不同的栈帧。

  有了上述的理论作为支撑,很容易得出下面的结论:递归时每一次函数调用都一个栈帧保存调用信息,递归实质上就是一条道走到黑,走到递归出口的时候,不能再继续往下走了,于是又只能原路返回。在从函数第一次调用到递归出口以及再从递归出口原路返回的过程中,只需要适当的插入一些其他操作(如二叉树的遍历中对节点的访问操作),就可以实现我们的目的。(二叉树的遍历例子见我的另一篇博客:https://blog.csdn.net/qq_15054345/article/details/84066830

  递归在自己调用自己的过程中,操作系统会为每一次调用都建立一个栈帧,但是这个操纵对程序员是透明(透明即不可见,计算机世界的透明都是对外提供一个功能接口,但是内部实现一般都不可见)的。所以这就造成了下面两个现象:1、递归对于初学者很难理解,2,递归次数过多或者没有递归出口的递归函数会造成栈溢出(C++的递归一般支持上万次的递归调用而不会造成栈溢出)。

  在数据量比较大的情况下,递归的效率是比较低的。这是因为递归做了很多重复的计算,这就又引出了另外一种算法思想:动态规划。动态规划其实就是把递归中的每一次计算结果用一个数组保存下来,下次再需要用到这个值的时候,直接从数组取值,而不是再重新算一遍,这就大幅度的提升了算法效率。实质上在算法界,目前我遇到的只有两种情况:要么是以更高的时间开销换取更低的空间开销,要么以更高的空间开销换取更低的时间开销(当然可能有其他的,欢迎补充和指教),不过实际中都是后者居多。上面这段话比较抽象,拿斐波那契数列为例吧,:

                                                 F(12) = F(11) + F(10)

                                                 F(11) = F(10) + F(9)

在计算F(12)的时候,需要计算一次F(11)和F(10),而在计算F(11)的时候还要计算一次F(10),并且F(12)和F(11)这两个计算中的F(10)可是算了两次的,并非共用的,这就是我说的重复计算。如果把F(i)的计算结果保存在一个数组中一个元素A[i]中,下次再次用到F(i)的计算结果时不再是傻乎乎的去算,而是直接从A[i]中取值,就能省去一大笔的时间开销,这就是动态规划的牛逼之处。

其实递归的优化方式还有一种叫做剪枝,有兴趣的可以搜一搜。我目前见识的剪枝还比较少,无法做出总结。

每个人在学算法的时候都是菜鸟,我当初学的时候还不知道如何下手,我并非计算机本专业的,也没有人指导,完全靠自己学以及上网看博客搜资料,中间走了很多弯路。现在我尽量把自己对算法的理解写下来,期望能帮助一些初学者。当然,若我的理解有错误之处,望指出来,算法路上与君共勉!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值