首先,要理解递归,不要去在脑子里想这个递归过程,因为很容易绕进去,人的大脑压入不了几个栈,要相信递归可以完成某个问题,否则就会陷入细节中,无法跳出来。
递归本质是自己调用自己,把大问题化成相同小问题的解决问题的思路,是一种编程技巧。
理解递归
举个例子,比如去电影院,你女朋友问你这是第几排,你问前一排人,前一排人也不知道,就再问前一个人,知道第一排,然后再一排一排的传回来,去问的过程是递,回来的过程是归,总结成代码就是
public int f(int n){
if (n==1){
return 1;
}
return f(n-1)+1;
}
递归二要素
- 相同子问题。大问题可以分解为规模不同,思路相同的小问题
- 存在终止条件
递归求解步骤
- 根据问题分解,寻找子问题,找规律写出递归公式
- 找到其中的终止条件
举个例子:
假如有N个台阶,每次可以走一个或者两个台阶,请问有多少种走法?
第一步走的时候会有两种走法,走一步或者走两步,那么N个台阶的走法就等于N-1个台阶的走法+N-2个台阶的走法。所以推导出公式
f(n)=f(n-1)+f(n-2)
下一步我们来找终止条件,当只有一个台阶的时候,那就只有一种走法了,即f(1)=1;当有两个台阶的时候,可以一次走一步或者一次走两步,这样f(2)=2;当有3个台阶的时候,可以分为f(2)+f(1),所以终止条件是f(1)=1,f(2)=2;
最终代码:
public int f(int n) {
if (n == 1) {
return 1;
}
if (n == 2) {
return 2;
}
return f(n - 1) + f(n - 2);
}
堆栈问题
函数调用会用栈来保存信息,每调用一个函数,就会将临时变量封装成栈帧压入栈中,函数执行结束才出栈返回,如果递归规模较大,会超出系统栈或者虚拟机栈空间。
提前在代码层面做好递归最大深度的控制
递归优化
递归过程中,有可能会出现重复子问题,例如菲波那切数列
我们可以通过利用hashMap将计算过的结果存储起来,这样就可以达到优化重复计算的问题。
非递归转换
本质上所有的递归代码都可以改为迭代循环的非递归写法。
递归代码虽然高效,但是需要注意堆栈溢出、重复计算、函数调用、空间复杂度高等。