0.写在前面
说明一下,本篇博客偏向于理论,一些例子例题看参考博客就好了。
1.简单理解递归
首先抛开你脑中对递归的想法,单纯先来看看这一个问题。
- 汉诺塔问题
汉诺塔问题描述的是:有三根柱子,在第一根柱子自下而上的按大小顺序添加圆盘,要求依次取出圆盘添加到另一根柱子上,但是要保持原有的结构,就是自下而上从大到小叠加,而且大圆盘不能放在比它小的圆盘上面,三根柱子之间一次只能移动一个圆盘。求将全部圆盘移动到另一根柱子需要移动几次?(参考资料中有图解版)。
具体过程就不分析了,参考资料有。要想一下子解出n个圆盘需要移动几次,这恐怕很有难度。或许我们可以试着先解出3个圆盘需要移动几次。通过操作的过程,很容易发现了,3个圆盘移动的次数是在移动了2个圆盘的次数上加上移动最后一个大圆盘。经过一些列操作可以发现,
移动n个圆盘次数=移动n-1个圆盘次数+移动第n个圆盘次数 - 谈谈递归
什么是递归:简单来说,在程序中就是方法调用方法本身。但是严格来说,递归其实是一种体现在算法中的思想。汉诺塔问题很显然就可以使用递归的算法去解决。在解决问题时,可以将一个复杂的问题分解成多个同样的子类问题(个人理解是逐层分解),然后解决某一层的问题时可以用前一层的结果。“将一个复杂的问题分解成多个同样的子类问题”可以理解为“递去”,“解决某一层的问题时可以用前一层的结果”可以理解为“归来”。“递去”其实就是一个不断分解问题的过程,“归来”就是一个不断解决问题的过程。
2.递归需要满足的三个条件
递归需要满足的三个条件,也就是说什么样的问题可以使用递归去解决。
1.一个问题的解可以分成多个子问题的解
2.这个问题与分解之后的子问题,除了数据规模,求解思路完全一样
3.存在递归终止条件
3.如何用递归算法解决问题
3.1 关注点
1.递归的终止条件
2.每一级递归需要做什么(既然能够使用递归算法来解决,就表示每级递归要做的事是一样的)
3.需要返回什么
3.2 例子
可以看参考资料中的第一篇。
在理解或者解决递归问题的时候,切记不要陷入去理解每调用一次方法的时候具体是什么样的。个人觉得在处理递归问题的时候还是应该从宏观的角度去理解。
还有所谓的递推公式其实就是每一级或者说每一层需要做的事。
4.使用递归的优缺点
4.1优点
递归代码有什么优点呢,优点就是递归代码表达力强,写法简洁。(相比缺点,个人感觉优点好微不足道)
4.2缺点
缺点就是空间复杂度高,有堆栈溢出的风险,存在重复计算等问题。在时间效率上,当函数调用的数量较大时,时间成本就很大。在空间上也会有很大的开销。
4.3还有值得注意的地方
- 递归代码要警惕堆栈溢出
编写递归代码的时候,可能会遇到堆栈溢出的问题。堆栈溢出会造成系统性崩溃。在函数调用的时候,Java虚拟机会使用栈来保存函数的临时变量。每调用一个函数,都会将临时变量封装为栈帧压入内存栈中,等函数执行完成返回时才会出栈。如果递归求解的数据规模很大,调用层次很深,一直压入栈,就会有堆栈溢出的风险。(感觉这块并没能理解很好,需要到时去学一下jvm再对比一下递归代码和非递归代码的区别,届时会补充)。 - 递归代码要警惕重复计算
在某些情况下,使用递归解法可能会存在重复计算的问题,这个具体可以看参考资料里面的第二篇。那么如何解决重复计算的问题呢?可以使用一个散列表来存储计算过的值,当需要计算某个值的时候,先去散列表中看下是否存在该值,如果不存在则开始计算。