递归算法时间复杂度分析与改善

递归算法大家都不陌生,当需要重复计算相同问题时,一般可以选择递归和循环两种算法。又因为递归实现起来代码比较简洁,所以通常都会使用递归来解决上述问题。比如斐波那契数列,再比如树的前序、中序、后续遍历算法。

递归算法虽然是有代码简洁这个优点,但是其缺点显著。因为递归函数是在执行过程中调用其自身,所以会占用大量的栈上空间,并且压栈和出栈都是有时间消耗的。所以从这一点上来看,递归的效率是不如循环。除了效率之外,递归还有一个相当明显的问题:可能会导致栈溢出。当递归调用的次数过大时,很有可能会出现栈溢出的情况。

我们这里暂不考虑空间复杂度,仅讨论其时间复杂度以及改善方法。

还是以经典的Fibonacci数列为例,其定义如下:

1. 递归解法

对于这个题目,大家对于其算法已经十分熟悉,很快就能写出下面的代码:

long long Fibonacci(unsigned int n)
{
    if (n <= 0) {
        return 0;
    }

    if (n == 1) {
        return 1;
    }

    return Fibonacci(n-1) + Fibonacci(n-2);
}

我们以f(10)为例分析来分析递归的计算过程。f(10)=f(9)+f(8), f(9)=f(8)+f(7), f(8)=f(7)+f(6)。。。。可以用树形结构来表示整个计算过程

我们可以看出来,上面的树中,很多结点都是重复计算的。事实上,递归算法的时间复杂度是n的指数级的。这样的复杂度,一般来说是不可接受的。

2. 递归算法改善

上述的递归算法中,时间复杂度高的原因是过程中存在大量的重复计算。因此,如果能想办法避免重复计算,那么其时间复杂度便可以降下来。

比较简单的方法是采用逆序的递归算法:f(0)+f(1)=f(2), f(1)+f(2)=f(3),以此类推便可以计算出f(n)。并且这个算法的时间复杂度很明显,就是O(n)。代码如下:

long long Fibonacci(unsigned int n)
{
    long long fibNMinusOne = 1;
    long long fibNMinusTwo = 0;
    long long fibN = 0;
    int result[2] = {0, 1};
    int i;

    if (n < 2) {
        return result[n];
    }

    for (i = 2; i < n; i++) {
        fibN = fibNMinusTwo + fibNMinusOne;
        fibNMinusTwo = fibNMinusOne;
        fibNMinusOne = fibN;
    }

    return fibN;
}

递归算法时间复杂度分析与改善

时间: 2024-10-11 20:49:59

递归算法时间复杂度分析与改善的相关文章

递归算法的时间复杂度分析 转载

在算法分析中,当一个算法中包含递归调用时,其时间复杂度的分析会转化为一个递归方程求解.实际上,这个问题是数学上求解渐近阶的问题,而递归方程的形式多种多样,其求解方法也是不一而足,比较常用的有以下四种方法: (1)代入法(Substitution Method)        代入法的基本步骤是先推测递归方程的显式解,然后用数学归纳法来验证该解是否合理.        (2)迭代法(Iteration Method)        迭代法的基本步骤是迭代地展开递归方程的右端,使之成为一个非递归的和

递归算法时间复杂度----汉诺塔

问题:汉诺塔递归算法时间复杂度 算法如下: 解释:size表示汉诺塔的规模,startStack表示汉诺塔起始,endStack 表示完成,midStack表示辅助 def Towers(size,startStack,endStack,midStack): if size == 1: print 'Move disk from ', firstStack, 'to ', endStack else: Towers(size-1,firstStack,midStack,endStack) Tow

C语言数组实现约瑟夫环问题,以及对其进行时间复杂度分析

尝试表达 本人试着去表达约瑟夫环问题:一群人围成一个圈,作这样的一个游戏,选定一个人作起点以及数数的方向,这个人先数1,到下一个人数2,直到数到游戏规则约定那个数的人,比如是3,数到3的那个人就离开这个游戏:按这样的规则,剩下一个人,游戏就结束,这个人就为赢家.(读者可以试着表达,不认同,直接忽略) 抽象分析 这个人就是一个数据个体,数据结点,数据元素.上面产生的数据结构为:单方向循环的链.可以用链表实现,也可以用数组来实现. 链表到数组的迁移 人(数据元素. 数据结点.数据个体) 结点关系 (

递归函数时间复杂度分析(转)

1.递归执行过程 (1) 例子:求N!. 这是一个简单的"累乘"问题,用递归算法也能解决. n! = n * (n - 1)! n > 1 0! = 1, 1! = 1 n = 0,1 因此,递归算法如下: Java代码 1 fact(int n) { 2 if(n == 0 || n == 1) 3 return 1; 4 else 5 return n * fact(n - 1); 6 } 以n=3为例,看运行过程如下: fact(3) ----- fact(2) -----

递归函数时间复杂度分析

(1) 递归运行过程    样例:求N!.     这是一个简单的"累乘"问题,用递归算法也能解决.     n! = n * (n - 1)!   n > 1     0! = 1, 1! = 1      n = 0,1     因此,递归算法例如以下:    Java代码 fact(int n) {      if(n == 0 || n == 1)            return 1;          else                return n * f

选择排序的时间复杂度分析

每一趟从待排序的数据元素中选出最小(或最大)的一个元素,顺序放在已排好序的数列的最前(最后),直到全部待排序的数据元素排完.选择排序是不稳定的排序方法. 选择排序是给每个位置选择当前元素最小的,比如给第一个位置选择最小的,在剩余元素里面给第二个元素选择第二小的,依次类推,直到第n-1个元素,第n个元素不用选择了,因为只剩下它一个最大的元素了.那么,在一趟选择,如果一个元素比当前元素小,而该小的元素又出现在一个和当前元素相等的元素后面,那么交换后稳定性就被破坏了.比较拗口,举个例子,序列5 8 5

合并排序算法时间复杂度分析

一.合并已排序的两个数组,依次比较两个数组元素大小,并按大小加入到暂存数组B,最后保存到A: Algorithm: MERGE(A, p, q, r) 输入:数组A[p...q]和A[q+1...r],各自按升序排列 输出:将A[p...q]和A[q+1...r]合并后的升序排序的新数组 01. s←p; t←q+1; k←p; {s, t, p 分别指向A[p...q], A[q+1...r]和暂存数组B} 02. while s≤q and t≤r 03. if A[s] ≤A[t] the

算法时间复杂度分析基础

摘要      本文论述了在算法分析领域一个重要问题--时间复杂度分析的基础内容.本文将首先明确时间复杂度的意义,而后以形式化方式论述其在数学上的定义及相关推导.从而帮助大家从本质上认清这个概念. 前言      通常,对于一个给定的算法,我们要做 两项分析.第一是从数学上证明算法的正确性,这一步主要用到形式化证明的方法及相关推理模式,如循环不变式.数学归纳法等.而在证明算法是正确的基础上,第二部就是分析算法的时间复杂度.算法的时间复杂度反映了程序执行时间随输入规模增长而增长的量级,在很大程度上

【转】.. Android应用内存泄露分析、改善经验总结

原文网址:http://wetest.qq.com/lab/view/107.html?from=ads_test2_qqtips&sessionUserType=BFT.PARAMS.194206.TASKID&ADUIN=554147273&ADSESSION=1467939955&ADTAG=CLIENT.QQ.5479_.0&ADPUBNO=26582 前言   通过这几天对好几个应用的内存泄露检测和改善,效果明显: 完全退出应用时,手动触发GC,从原来占有