【转】递归函数时间复杂度分析

摘要:
递归回溯递归算法在操作中不断调用自己的规模缩减过程;也就是说,当它递归到事实(1)时,它开始回溯(返回调用算法)并计算。从事实(1)=1,它返回事实(2),从2*事实(1”=2,它返回到事实(3);直接简单递归调用;直接复杂递归调用;间接递归调用;

(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 * fact(n - 1);  
    }  
    以n=3为例,看运行过程如下: 
    fact(3) ----- fact(2) ----- fact(1) ------ fact(2) -----fact(3) 
    ------------------------------>  ------------------------------> 
                递归                            回溯 
  递归算法在运行中不断调用自身降低规模的过程,当规模降为1,即递归到fact(1)时,满足停止条件停止递归,开始回溯(返回调用算法)并计算,从fact(1)=1计算返回到fact(2);计算2*fact(1)=2返回到fact(3);计算3*fact(2)=6,结束递归。 
   算法的起始模块也是终止模块。 
(2) 递归实现机制 
    每一次递归调用,都用一个特殊的数据结构""记录当前算法的执行状态,特别地设置地址栈,用来记录当前算法的执行位置,以备回溯时正常返回。递归模块的形式参数是普通变量,每次递归调用得到的值都是不同的,他们也是由""来存储。 
(3) 递归调用的几种形式 
    一般递归调用有以下几种形式(其中a1a2b1b2k1k2为常数)。 
   <1> 直接简单递归调用: f(n) {...a1 * f((n - k1) / b1); ...}; 
    
   <2> 直接复杂递归调用: f(n) {...a1 * f((n - k1) / b1); a2 * f((n - k2) / b2); ...}; 
    <3> 间接递归调用:  f(n) {...a1 * f((n - k1) / b1); ...}, 
                        g(n) {...a2 * f((n - k2) / b2); ...}。 
2. 递归算法效率分析方法 
   递归算法的分析方法比较多,最常用的便是迭代法。 
  迭代法的基本步骤是先将递归算法简化为对应的递归方程,然后通过反复迭代,将递归方程的右端变换成一个级数,最后求级数的和,再估计和的渐进阶。 
  <1> 例:n! 
       算法的递归方程为: T(n) = T(n - 1) + O(1); 
       迭代展开: T(n) = T(n - 1) + O(1) 
                       = T(n - 2) + O(1) + O(1) 
                       = T(n - 3) + O(1) + O(1) + O(1) 
                       = ...... 
                       = O(1) + ... + O(1) + O(1) + O(1) 
                       = n * O(1) 
                       = O(n) 
      这个例子的时间复杂性是线性的。 
<2> 例:如下递归方程: 
      
      T(n) = 2T(n/2) + 2, 且假设n=2k次方。 
      T(n) = 2T(n/2) + 2 
           = 2(2T(n/2*2) + 2) + 2 
           = 4T(n/2*2) + 4 + 2 
           = 4(2T(n/2*2*2) + 2) + 4 + 2 
           = 2*2*2T(n/2*2*2) + 8 + 4 + 2 
           = ... 
           = 2(k-1)次方 * T(n/2(i-1)次方) + $(i:1~(k-1))2i次方 
           = 2(k-1)次方 + (2k次方)  - 2 
           = (3/2) * (2k次方) - 2 
           = (3/2) * n - 2 
           = O(n) 
      这个例子的时间复杂性也是线性的。 
<3> 例:如下递归方程: 
      
      T(n) = 2T(n/2) + O(n), 且假设n=2k次方。 
      T(n) = 2T(n/2) + O(n) 
           = 2T(n/4) + 2O(n/2) + O(n) 
           = ... 
           = O(n) + O(n) + ... + O(n) + O(n) + O(n) 
           = k * O(n) 
           = O(k*n) 
           = O(nlog2n) //2为底 
     
      一般地,当递归方程为T(n) = aT(n/c) + O(n), T(n)的解为: 
      O(n)          (a<c && c>1) 
      O(nlog2n)     (a=c && c>1) //2为底 
      O(nlogca)     (a>c && c>1) //n(logca)次方,以c为底 
   上面介绍的3种递归调用形式,比较常用的是第一种情况,第二种形式也有时出现,而第三种形式(间接递归调用)使用的较少,且算法分析 
比较复杂。 下面举个第二种形式的递归调用例子。 
  <4> 递归方程为:T(n) = T(n/3) + T(2n/3) + n 
     为了更好的理解,先画出递归过程相应的递归树: 
                            n                        --------> n 
                    n/3            2n/3              --------> n 
              n/9       2n/9   2n/9     4n/9         --------> n 
           ......     ......  ......  .......        ...... 
                                                     -------- 
                                                     总共O(nlogn) 
     累计递归树各层的非递归项的值,每一层和都等于n,从根到叶的最长路径是: 
    
      n --> (2/3)n --> (4/9)n --> (12/27)n --> ... --> 1 
     设最长路径为k,则应该有: 
      
     (2/3)k次方 * n = 1 
     得到 k = log(2/3)n  // (2/3)为底 
     于是 T(n) <= (K + 1) * n = n (log(2/3)n + 1) 
     即 T(n) = O(nlogn) 
    由此例子表明,对于第二种递归形式调用,借助于递归树,用迭代法进行算法分析是简单易行的。

免责声明:文章转载自《【转】递归函数时间复杂度分析》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇倍福TwinCAT(贝福Beckhoff)常见问题(FAQ)-如何在初始化的时候写入参数内存地址与内存空间下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

关于项目架构的一些浅谈

         最近,一直在学习和摸索关于项目架构的东东。或许说架构说得有点太大。但是还是暂且用着吧。 也看看过几个高手关于三层架构和MVC模型的文章,觉得很多东西的理解和自己的不是很一样。但是自己确实没有他们研究的深入,所以也不妄加评论。         在这里想说的是,自己幼稚的观点欢迎各位砸砖;自己绝对的言语只是针对自己的想法。         我...

Apriori 算法-如何进行关联规则挖掘

公号:码农充电站pro主页:https://codeshellme.github.io 在数据分析领域有一个经典的故事,叫做“尿布与啤酒”。 据说,在美国西部的一家连锁超市发现,很多男人会在周四购买尿布和啤酒。这样超市就可以将尿布与啤酒放在一起卖,便可以增加销售量。 “尿布与啤酒”这个案例就属于数据分析中的关联分析,也就是分析数据集中的内在隐含关系。 关联...

NLP常用Python开发工具

一、Numpy NumPy系统是Python的一种开源的数值计算包。 包括: 1、一个强大的N维数组对象Array; 2、比较成熟的(广播)函数 库; 3、用于整合C/C++和Fortran代码的工具包; 4、实用的线性代数、傅里叶变换和随机数生成函数。 numpy和稀疏矩阵运算包scipy配合使用更加方便。 安装: pip install numpy 二...

C# 中采用treeview递归生成目录树(Winform和Webform两种)

部门表: 课程表: 查询结果结构: 数据结构分析,部门分为部门id和部门名称;课程分为课程id,课程名称,课程路径和课程所属部门。 要求以部门为父节点展示不同部门下的课程。 Winform采用treeview递归生成目录树using System;using System.Collections.Generic;using System.Comp...

Oracle 存储过程,触发器,事务,锁

1.1存储过程   存储过程是一种命名的PL/SQL程序块,他可以有参数,也可以有若干个输入、输出参数。甚至可以有多个即做输入又做输出的参数,但他都没有返回值。存储过程被保存在数据库中,他不可以被SQL语句直接执行调用。通过EXECUTE命令或在PL/SQL命令中调用,因为存储过程是已经编译好的代码块,所以被调用或引用时,执行效率很高。 1.1.1 存储过...

leetcode常规算法题复盘(第七期)——区间和的个数(附带排序算法归纳)

题目原文   327. 区间和的个数   给定一个整数数组 nums,返回区间和在 [lower, upper] 之间的个数,包含 lower 和 upper。区间和 S(i, j) 表示在 nums 中,位置从 i 到 j 的元素之和,包含 i 和 j (i ≤ j)。 说明:最直观的算法复杂度是 O(n2) ,请在此基础上优化你的算法。 示例: 输入...