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


    (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) 
        由此例子表明,对于第二种递归形式调用,借助于递归树,用迭代法进行算法分析是简单易行的。

  • 相关阅读:
    8.09_python_lx_shopping
    8.06_python_lx_day25
    8.05_python_lx_day24
    8.05_python_lx_day23
    8.03_python_lx_day21<2>
    Java注解
    Java多线程04
    Java多线程03
    Java多线程02
    Java多线程01
  • 原文地址:https://www.cnblogs.com/hahawgp/p/2917717.html
Copyright © 2020-2023  润新知