关于递归的效率的问题
最近在学习算法的时候,学到了不少用递归实现 却优于其他的算法 的新方法。于是我心中产生了疑问,以前听说过一句话说:“可以用迭代实现的就不用递归”,因为人们对于递归的认知就是 其效率低下,比如我当时在实现斐波那契数列用了经典的递归方法。求导第10+个数的时候,就明显感受到了递归耗时巨大的特点。
但是我们也不乏认识到许多使用递归实现,但是效率却是同等问题的解决方法中较优的算法:
- 堆排序 o(nlogn)
- 归并排序 o(nlogn)
- 二分查找 o(logn)
- 其他使用分治策略的算法
如下图我们可以看到,在经典排序算法里面,平均性能最优的就是堆排序和归并排序

图片参考自:blog.csdn.net/yuxin6866/a…
为什么递归没有想象中的慢?
要解决这个问题,我们先要弄清楚,为什么递归“效率低”? 主要的原因有一下几点:
- 函数调用的开销导致的
在一个函数调用之前需要做许多工作,如准备函数内局部变量使用的空间、保存函数的参数,记录函数调用位置等等,每递归一次都需要做这些事情,因此会产生额外开销导致递归效率偏低。如果递归调用N次,就要分配N* 局部变量、 N* 形参、 N* 调用函数地址、 N*返回值
- 针对于不同的问题,其递归解法可能是相对低效的
这个低效主要在于这个问题的算法本身。比如说求斐波那契的某一项,子问题会大量重复出现,会产生很多重复计算,导致了效率低下
- 不断入栈出栈操作
- 栈容量的限制,可能导致stack overflow
知道了递归的缺点之后,我们在考虑一下,递归的优势在哪里?
- 递归本身是描述问题的一种很好的方式,易于理解
- 对于实现难度很高的算法,使用递归将大大减少工作量
总结一下,那么我们为什么经常使用递归解决问题?,尽管它“效率低”?
- 我们在讨论递归的低效或者它的缺点的时候,实际上我们讲述的是它在执行这个层面上,比循环相比,消耗了额外的计算机资源。并不是在算法优劣这个层面上来讨论。也就是理论上讲,用递归的思路去写算法,完全可以实现像循环一样的算法效率
如树的遍历算法,是最经常使用递归算法的地方之一
通过寥寥三行代码就可以实现一整棵树的遍历操作,时间复杂度为O(n)(没有比遍历n个节点更低的时间了)
而手动使用栈来实现遍历,其时间复杂度也为O(n)
虽然非递归算法在执行起来不需要频繁的函数调用操作,可以提高执行速度
但是递归的代码也有易于实现的特点
- 实际上,每种方法都有其适用范围,对于递归和非递归的选择,得考虑到实际问题,有时递归会和非递归程序运行时间相差无几
- 使用递归的一个好处是大大减少了我们的代码复杂度,实际上考虑算法优化时,也要讲究是否值得优化,我们知道计算机1s的运行次数可以达到十几亿次,对于一个不明显的优化问题,我们是否投入人的精力对其进行修改,也是一个值得权衡的问题。递归可以带给我们简洁之美往往体现在它对程序员编程的友好
对于第二点,有如下的一个拓展思考:
假设我们只用循环去代替所有的递归,用循环效率会比递归效率高吗?
递归与循环是两种不同的解决问题的典型思路。当然也并不是说循环效率就一定比递归高,递归和循环是两码事,递归带有栈操作,循环则不一定,两个概念不是一个层次,不同场景做不同的尝试