数据结构与算法:递归

1,116 阅读3分钟

一、什么是递归

递归是一种应用非常广泛的算法(或者说编程技巧)。很短数据结构和算法的编码实现都要用到递归,比如DFS深度优先搜索、前后中序二叉树遍历等等。

方法或者函数调用自身的方式称为递归调用,调用称为,返回称为

二、为什么使用递归

递归的优点:代码表达能力强,写起来简洁,执行起来高效。

递归的缺点:空间复杂度高、有堆栈溢出的风险、存在重复计算、过多的函数调用会耗时较多

三、什么样的问题可以使用递归

递归使用的个条件:

1.一个问题的解可以拆解成为几个子问题的解;子问题就是数据规模更小的问题

2.此问题与分解之后的子问题,除了数据规模不同,求解思路完全一样

3.存在递归终止条件,即不存在无限循环

四、如何实现递归

写递归代码的关键是找到如何将大问题分解为小问题规律,写出递推公式,找到终止条件,最后将递推公式和终止条件翻译成代码。

避免思维误区:不要试图想清楚递归每一步的执行,这种试图想清楚整个递和归过程的想法,就是一个思维误区。

正确的思维方式:如果一个问题A可以分解为子问题B、C、D,可以假设B、C、D已经解决,在此基础上思考如何解决问题A。并且,只需要思考问题A和子问题B、C、D、两层之间的关系即可,不需要一层一层往下思考子问题与子子问题,屏蔽掉递归细节,这样,理解起来就比较简单了。

因此,只要遇到递归,就把它抽象成一个递推公式,不用想一层层的套用关系,不要试图用人脑去分解递归的每个步骤

五、递归的常见问题以及解决方案

递归代码要警惕堆栈溢出:函数调用会使用栈来存储临时变量,每调用一个函数,都会将临时变量封装为栈帧压入内存找,等到函数返回时,才会出栈。系统栈或者虚拟机栈空间一般都不大。如果递归求解的数据规模很大,调用层次很深,一直压入栈,就会有堆栈溢出的风险。

可以通过限制递归调用的最大深度来解决这个问题。

递归代码要警惕重复计算:为了避免重复计算,我们可以通过一个数据结构(比如散列表)来保存已经求解过的f(k)。当递归调用到f(k)时,先看是否已经求解过了。如果是,直接从散列表中取值返回,不需要重复计算。


小结:

递归是一种非常高效、简洁的编码技巧。只要满足“三个条件”的问题就可以通过递归代码来解决。

编写递归代码的关键是不要把自己绕进去,正确姿势是写出递推公式,找出终止条件,然后再翻译成代码

递归调用虽然简洁高效,但是递归代码也有弊端。比如堆栈溢出重复计算函数调用耗时多空间复杂度高等。写递归代码的时候。一定要控制好这些副作用