算法与数据结构之美-递归

算法与数据结构之美-递归

  • 如何理解递归?
  • 递归需要满足的条件
  • 如何编写递归代码?
  • 递归警告
    • 警惕堆栈溢出
    • 警惕重复计算
  • 内容小结

如何理解递归?

递归是一种应用非常广泛的算法,许多数据结构的编码实现都需要递归,例如DFS深度优先搜素、前中后序二叉树遍历等等。

在生活中,周末去看电影,但是不知道自己是在第几排,这个时候你就会问前面一个人他是第几排,前面的人要是不知道,就会再问他前面的人,直到第一排的人。然后再依次返回自己的排数,你就知道了答案。

递归,去的过程是传递,来的过程是回归,而电影院座位的问题,可以用递推公式来表示:
f(n) = f(n-1)+1,f(1) = 1;

递归需要满足的条件

  • 一个问题的解可以分为几个子问题的解
  • 该问题与分解后的子问题,除了数据规模不同,求解思路完全一样
  • 存在递归终止条件

如何编写递归代码?

递归代码的关键是递推公式,记住这一点才能保证自己能够写出正确的递归代码。

例子:
假设有n个台阶,一次可以上1个台阶或者2个台阶,请问走完这n个台阶有多少种走法?

思路:
思考一下,上台阶的走法其实可以根据第一步走1个台阶还是2个台阶分为两类,第一步是1个台阶的走法为f(n-1),第一步是2个台阶的走法是f(n-2):
所以f(n)= f(n-1)+f(n-2),f(1) = 1,f(2)=2;代码就不写了,这里有一点想说的是,我们在进行递归的时候,总是尝试在脑海里模拟整个递归过程,但其实分解一下,脑子就绕晕了。在遇到递归问题时,首先要能将问题分解成子问题,然后推导出递推公式,就不用靠自己去分解递归的每个步骤。

递归警告

警惕堆栈溢出

函数会使用栈来保存临时变量,每调用一个函数,都会将临时变量封装为栈帧压入内存栈,等函数执行完毕之后,出栈。系统栈或者虚拟机栈空间一般都不大,如果递归求解的数据规模很大,调用层次很深的话,一直压入栈,就会有堆栈溢出的风险。

例如:我们将系统栈或者JVM堆栈大小设置为1KB,在求解f(19999)时便会出现如下堆栈报错:java.lang.StackOverflowError;

警惕重复计算

算法与数据结构之美-递归_第1张图片
从上图可以看出,在递归分解过程,会有很多重复计算的问题;为了避免重复计算,可以通过散列表来保存已经求解过的f(k)。除此之外,在递归过程中调用函数的数量过大时,就会增加空间和时间复杂度。

内容小结

递归是一种高效的编码技巧,只需要满足“三个条件”就可以通过递归来实现,需要正确的推导出递推公式,找出终止条件,在翻译成递归代码。递归代码虽然高效,但是也是有很多弊端的,需要警惕堆栈溢出、重复计算、函数调用超时等,所以需要考虑这些情况。

你可能感兴趣的:(数据结构与算法之美)