一、什么是递归
所谓递归,简单点来说,就是一个函数直接或间接调用自身的一种方法,它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解。
引用知乎大佬的例子:
我们可以把” 递归 “比喻成 “查字典 “,当你查一个词,发现这个词的解释中某个词仍然不懂,于是你开始查这第二个词。
可惜,第二个词里仍然有不懂的词,于是查第三个词,这样查下去,直到有一个词的解释是你完全能看懂的,那么递归走到了尽头,然后你开始后退,逐个明白之前查过的每一个词,最终,你明白了最开始那个词的意思。
我们把查字典理解成一个函数search(){}
,而“明白了”就是停止条件。
按这个思路,那这个流程就是这样的:
public void search(){
//如果明白了就停止函数
if("明白了"){
return;
}
//没明白调用自己继续查
search();
}
我们举个简单的例子:
要计算阶乘1*2*3*.....*(n-1)*n
,代码如下:
public static int mult(int n) {
//终止条件,当n=1时直接返回1
if (n == 1){
return n;
}
//计算n*(n-1).....
return n * mult(n - 1);
}
二、递归和栈的关系
递归的过程就是出入栈的过程
递归的问题实际上都能拆分成出入栈问题,我们可以举上面计算1*2*3*.....*(n-1)*n
这个例子来理解一下:
如果n=4,那么过程就是这样:
- mult(4)调用了mult(3)
- mult(3)调用了mult(2)
- mult(2)调用了mult(1)
- 到了mult(1)时满足了终止条件,返回结果
用出入栈的思维理解:
- 步骤1-3都是一个入栈过程,mult(4)计算得出结果后入栈,然后运行mult(3)得出结果,然后在入栈......以此类推
- 当到达n=1的停止条件时递归停止不再入栈,此时栈深度就是4,这也叫递归深度
- 满足停止条件后出栈,mult(1)的结果出栈,与mult(2)的结果出栈相乘,再与随后出栈的mult(3)的结果相乘.....以此类推
递归的本质就是栈的出入过程,所以实际上当深度过深,超过了jvm规定允许的栈最大深度的时候,就会出现栈溢出的问题,也就是java里的*Error
三、递归的使用条件
那么,我们是时候可以使用递归来解决问题呢:
- 当问题可以拆分为子问题,并且子问题与原问题解决方法相同
- 有一个明确的程序停止条件
比如之前的文章中提到连续乘除问题就是一个典型的例子。