1、递归的定义

  • 函数直接或间接的调用自己
  • 使用递归时,必须有明确的结束递归的条件

2、递归的适用场合

  • 数据的定义按照递归定义(比如求n!)
  • 问题的解法适用于使用递归
  • 数据的结构是按递归定义的(比如二叉树)

3、线性递归

  也就是普通递归,下一次递归数据的计算要依赖于上一次递归的结果和参数,当数据量较小时执行效率与尾递归几乎没区别,但当数据量较大,迭代次数较多时,由于每次递归都要在内存中开辟一个栈空间,用来存储上次递归的结果和参数,这样的算法将导致严重的内存开销,甚至造成内存溢出,抛出java.lang.StackOverflowError,下面用线性递归实现斐波那契数列。

  /**

* 线性递归实现斐波拉契数列

* @param month

*/

public  int fblq(int month){

if(month<3){

return 1;

}

return fblq(month-1)+fblq(month-2);//每步递归都严重依赖上一次递归结果,并占用大量的栈区空间,执行效率极低

}

 

测试上段程序运行时间

@Test

public  void test(){

long startTime = System.currentTimeMillis();

System.out.println(fblq(50));

long endTime = System.currentTimeMillis();

System.out.println("程序运行时间:" + (endTime - startTime) + "ms");    //输出程序运行时间

}

   程序运行时间:43643ms(非常慢),递归所需栈区空间,以指数级进行增长

4、尾递归

  如果一个函数中所有递归形式的调用都出现在函数的末尾,我们称这个函数是尾递归的。尾递归函数的特点是在回归过程中不用做任何操作,这个特性很重要,因为大多数现代的编译器会利用这种特点自动生成优化的代码。

 

/**
* 斐波那契数列尾递归
*/
public int fblq(int n,int num1,int num2){
if(n==1){
return num1;
}
return fblq(n-1,num2,num1+num2);
}

 

@Test
public void Test37(){
long startTime = System.currentTimeMillis();
System.out.println(fblq(50,1,1));
long endTime = System.currentTimeMillis();
System.out.println("程序运行时间:" + (endTime - startTime) + "ms"); //输出程序运行时间
}

 

程序运行时间:1ms,递归以线性关系增长

 

总结:在能使用尾递归时尽量使用尾递归,这样不仅能节省内存资源,而且执行效率更高,但是相比于普通for循环,递归的效率是较低的,所以再不是非递归不可的环境中,尽量使用普通循环代替递归算法。