什么是递归?
一个方法直接或间接的调用自己,适用于把一个大型的复杂问题层层转化为一个与原问题相似的规模较小的问题求解。
例:求1+2+3+…+N
将原问题划分为若干小问题
假设:sum(n)表示1+2+3+…+n-1+n==>sum(n-1)+n;
sum(n-1)= 1+2+…+n-2+n-1==>sum(n-2)+n-1;
…
sum(2)=sum(1)+2;
sum(1)=1(此时问题可以直接求解,即递归的出口)
最终
public static long sum(int n){
if (n==1){
return 1;
}else {
return sum(n-1)+n;
}
}
递归的实现条件
1.对原问题进行拆分,拆分成一个个的小问题,而且小问题还要与原问题解法相同
2.必须要有递归的出口
递归执行过程分析
递归的程序的执行过程不太容易理解, 要想理解清楚递归, 必须先理解清楚 “方法的执行过程”, 尤其是 “方法执行结束之后, 回到调用位置继续往下执行”.
代码示例: 递归求 N 的阶乘
public static void main(String[] args) {
int n = 5;
int ret = factor(n);
System.out.println("ret = " + ret);
}
public static int factor(int n) {
System.out.println("函数开始, n = " + n);
if (n == 1) {
System.out.println("函数结束, n = 1 ret = 1");
return 1;
}
int ret = n * factor(n - 1);
System.out.println("函数结束, n = " + n + " ret = " + ret);
return ret; }
// 执行结果
函数开始, n = 5
函数开始, n = 4
函数开始, n = 3
函数开始, n = 2
函数开始, n = 1
函数结束, n = 1 ret = 1
函数结束, n = 2 ret = 2
函数结束, n = 3 ret = 6
函数结束, n = 4 ret = 24
函数结束, n = 5 ret = 120
ret = 120
执行过程图
程序按照序号中标识的 (1) -> (8) 的顺序执行.
递归的优缺点
优点
1.让代码简洁
2.在树的前序,中序,后序遍历算法中,递归的实现明显要比循环简单得多。
缺点
1.效率低
递归由于是函数调用自身,而函数调用是有时间和空间的消耗的:每一次函数调用,都需要在内存栈中分配空间以保存参数、返回地址以及临时变量,而往栈中压入数据和弹出数据都需要时间。递归中很多计算都是重复的,由于其本质是把一个问题分解成两个或者多个小问题,多个小问题存在相互重叠的部分,则存在重复计算,如fibonacci斐波那契数列的递归实现。
2.可能出现栈溢出
调用栈可能会溢出,其实每一次函数调用会在内存栈中分配空间,而每个进程的栈的容量是有限的,当调用的层次太多时,就会超出栈的容量,从而导致栈溢出。
尾递归
尾递归,比线性递归多一个参数,这个参数是上一次调用函数得到的结果;所以,关键点在于,尾递归每次调用都在收集结果,避免了线性递归不收集结果只能依次展开消耗内存的坏处。
例如:求斐波那契数列
public static int fib_rec(int n,int a,int b){
if (n==1){
return a;
}else {
return fib_rec(n-1,b,a+b);
}
}
public static void main(String[] args) {
System.out.println(fib_rec(5,1,1));
}
执行结果:5