尾递归和线性递归

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循环,递推的效率是较低的,所以再不是非递推不可的环境中,尽量使用普通循环代替递归算法。

时间: 2024-12-08 17:28:36

尾递归和线性递归的相关文章

线性递归和尾递归

线性递归,就是大家平常说的递归,线性递归函数的最后一步操作不是递归操作,将最终条件代入计算.在每次递归调用时,递归函数中的参数,局部变量等都要保存在栈中,当数据量很大的时候,会造成栈溢出. 尾递归,也就是线性迭代,尾递归函数的最后一步操作是递归,也即在进行递归之前,把全部的操作先执行完,这样的好处是,不用花费大量的栈空间来保存上次递归中的参数.局部变量等,这是因为上次递归操作结束后,已经将之前的数据计算出来,传递给当前的递归函数,这样上次递归中的局部变量和参数等就会被删除,释放空间,从而不会造成

尾递归与普通递归

这篇文章总结尾递归,普通递归的区别,以及使用递归时需要注意的问题. 1. 递归定义 递归有四条基本准则,摘抄自艾伦韦斯的<数据结构与算法分析--C语言描述>. 1)基准情形:可以理解为无须递归就可以解出的结果,或者说是递归的最终到达点,或者说是初始条件. 2)不断推进:不断的循环,直到达到基准情形. 3)设计法则:所有的递归调用能正常运行. 4)合成效益法则:求解一个问题的同一实例时,切勿在不同的递归调用中做重复性的事情. 下面给出例子来说明四条准则. 1)如下就没有初始条件,或者说是结束条件

Java 递归、尾递归、非递归 处理阶乘问题

n!=n*(n-1)! import java.io.BufferedReader; import java.io.InputStreamReader; /** * n的阶乘,即n! (n*(n-1)*(n-2)*...1). * 0!为什么=1,由于1!=1*0!.所以0!=1 * * @author stone * @date 2015-1-6 下午18:48:00 */ public class FactorialRecursion { static long fact(long n) {

Java 递归、尾递归、非递归、栈 处理 三角数问题

import java.io.BufferedReader; import java.io.InputStreamReader; //1,3,6,10,15...n 三角数 /* * # 1 * ## 1+2 * ### 1+2+3 * #### 1+2+3+4 * ##### 1+2+3+4+5 * ...第1层为1, 第n层等于 n + (f(n-1)) */ public class TriangleNumber { static int triangle(int n) { if (n <

机器学习之线性递归

一.先弄清楚机器学习的几个概念: 训练集:训练样本,每个样本都由表示要学习的特征集(输入变量)和目标(输出变量) 设训练样本数为m 输入特征\变量input : x  其中每个样本用 表示(第i个样本),样本中每个特征/输入用表示(第i个样本中的第j个特征) 输出特征/变量output : y     其中每个样本用 表示(第i个样本),每个样本只有一个输出. 更新参数/权值 weight : theat   每个输入x对应一个theta, 用表示 因此第i个样本集用表示. 二.监督的机器学习算

递归与迭代_2 2016.4.22

八.递归消除 按照递归的思想可使我们得以从宏观上理解和把握应用问题的实质 深入挖掘和洞悉算法过程的主要矛盾和一般性模式 并最终设计和编写出简洁优美且精确紧凑的算法 然而,递归模式并非十全十美,其众多优点的背后也隐含着某些代价 (1)空间成本 首先,从递归跟踪分析的角度不难看出,递归算法所消耗的空间量主要取决于递归深度 故较之同一算法的迭代版,递归版往往需耗费更多空间,并进而影响实际的运行速度 另外,就操作系统而言,为实现递归调用需要花费大量额外的时间以创建.维护和销毁各递归实例,这些也会令计算的

递归函数详细解析

/*题目描述 输入一颗二叉树和一个整数,打印出二叉树中结点值的和为输入整数的所有路径. 路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径.*/ /* struct TreeNode { int val; struct TreeNode *left; struct TreeNode *right; TreeNode(int x) : val(x), left(NULL), right(NULL) { } };*/ class Solution { public: void fin

[转]递归算法详解

计算机科学的新学生通常难以理解递归程序设计的概念.递归思想之所以困难,原因在于它非常像是循环推理(circular reasoning).它也不是一个直观的过程:当我们指挥别人做事的时候,我们极少会递归地指挥他们. Introduction 递归算法是一种直接或者间接调用自身函数或者方法的算法.递归算法的实质是把问题分解成规模缩小的同类问题的子问题,然后递归调用方法来表示问题的解.递归算法对解决一大类问题很有效,它可以使算法简洁和易于理解.递归算法,其实说白了,就是程序的自身调用.它表现在一段程

递归与尾递归总结

  TAIL_RECURSIVE_QUICKSORT(A,p,r) while p<r //Partiton and sort left subarray q = PARTITION(A,p,r) TAIL_RECURSIVE_QUICKSORT(A,p,q-1) p = q+1  转自.http://www.cnblogs.com/Anker/archive/2013/03/04/2943498.html 1.递归 关于递归的概念,我们都不陌生.简单的来说递归就是一个函数直接或间接地调用自身,