软考 递归式时间复杂度计算详解

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

在算法分析中,当一个算法中包含递归调用时,其时间复杂度的分析会转化为一个递归方程求解。实际上,这个问题是数学上求解渐近阶的问题,而递归方程的形式多种多样,其求解方法也是不一而足,比较常用的有以下四种方法:

方法一:代换法

代换法主要需要以下两个步骤

1、  猜答案,不需要完全猜出来,不需要知道常熟系数的准确值,而只需要猜出它的形式,比如猜一个递归式的时间复杂度大概是O(n2),即它的运行时间应该是一个常熟乘以n2,可能还会有一些低阶项。


 

方法二:递归树法

递归树法主要是通过递归树将递归式展开来找到答案,然后再用代换法证明它,因为递归树法是不严谨的。

例如,用递归树法求T(n) = T(n/2) + n2 , 用递归树法将该递归式展开

像这样将递归树展开并延伸下去,最终到叶子节点就只剩下T(1),那么该递归树的高度就是logn,因为从顶点n出发,到n/2,到n/4,……最后到1,那么从n到1的折半次数是logn,即高度是logn(应该是一个常数乘以logn,不过没多大关系)。而最下面叶子节点的数目是n,因为从第一层往下,节点数变化为1,2,4,8……,如果树的高度是h,那么就会有2h个叶节点,而高度是logn,那么2logn=n。那么,整体所做的工作加起来就是T(n)了

T(n) = [1+1/2+1/4+……]n2 = 2n2,于是可知时间复杂度为T(n) = O(n2)。

再例如,用递归树法求T(n) = T(n/4)+T(n/2)+n2 ,下面用递归树的方法将该递归式展开

最后,求叶子节点的数目有点麻烦,因为分支的递归速度是不一样的,左边降低到n/16的时候,右边才降低到n/4,左边子树的高度将会比右边子树的高度要小。可以看到叶子节点的数目必然小于n,因为最开始的问题大小是n,然后递归成一个n/4和n/2的两个子问题,直到最后递归到1停止,而n/4+n/2 < n , 所以最后叶子节点的数目不会超过n,将每层求和就得到T(n),经过观察发现一个等比数列,于是数学归纳法开始派上用场

T(n) = (1+5/16+25/256+……)n2≤2n2 =O(n2)   于是得到该递归式时间复杂度为O(n2),因为是猜出来的等比数列,于是需要用数学归纳法证明之,就又变成方法一中代换法求证了。


方法三:主方法 (master method)

该方法仅适用于特定格式的递归式

同时要求f(n)渐进趋正,即当n->无穷时,f(n)>0。(我觉得上图中第2,3中少了个logn,具体请参考算法导论一书中对时间复杂度的讨论)

例如:

T(n)=4T(n/2)+n , 则a=4,b=2,f(n)=n,计算nlog(b,a)=n2>f(n), 满足模式一,因此T(n) = nlog(b,a)=O(n2)

T(n)=4T(n/2)+n2,则根据上面计算,满足模式二,因此T(n)=O(n2logn)

T(n)=4T(n/2)+n3,满足模式三,T(n)=O(n3)

对于该方法的正确性,可以通过递归树的方法证明,懒的画了,可以在大脑里构思出这样一个草图:

在第一层,f(n)分解为a个子问题,每个子问题都是f(n/b),第二层每个子问题又分解为a个子问题,每个问题都是f(n/b2)……这样递归分解下去,最后的叶子还是O(1),整个树的高度就是log以b为底n的对数,整个的叶子节点数目为a的log以b为底n的对数次方(alog(b,n)),即nlog(b,a)个叶子节点。每个分支的递减速度是一样的,将每层都加在一起便得到T(n)
,此时就需要对f(n)的情况进行讨论(于是就是上面的1,2,3)。

 

时间: 2024-11-08 22:53:48

软考 递归式时间复杂度计算详解的相关文章

Android编程之软键盘的隐藏显示实例详解_Android

本文实例分析了Android编程之软键盘的隐藏显示方法.分享给大家供大家参考,具体如下: Android是一个针对触摸屏专门设计的操作系统,当点击编辑框,系统自动为用户弹出软键盘,以便用户进行输入. 那么,弹出软键盘后必然会造成原有布局高度的减少,那么系统应该如何来处理布局的减少?我们能否在应用程序中进行自定义的控制?这些是本文要讨论的重点. 一.软键盘显示的原理 软件盘的本质是什么?软键盘其实是一个Dialog! InputMethodService为我们的输入法创建了一个Dialog,并且将

java 无限递归的构造器实例详解

在一些情况下,程序可导致构造器进行无限递归,如:  public class ConstrucorRecursion {  {   ConstrucorRecursion rc = new ConstrucorRecursion();  }  public ConstrucorRecursion()   {     System.out.println("程序执行无参数的构造器");   }  public static void main(String[] args)  {   Co

In-Stream Big Data Processing流式大数据处理详解

相当长一段时间以来,大数据社区已经普遍认识到了批量数据处理的不足.很多应用都对实时查询和流式处理产生了迫切需求.最近几年,在这个理念的推动下,催生出了一系列解决方案,Twitter Storm,Yahoo S4,Cloudera Impala,Apache Spark和Apache Tez纷纷加入大数据和NoSQL阵营.本文尝试探讨流式处理系统用到的技术,分析它们与大规模批量处理和OLTP/OLAP数据库的关系,并探索一个统一的查询引擎如何才能同时支持流式.批量和OLAP处理. 在Grid Dy

CubeMap视线反射方向计算详解

其基本原理很多例子上有讲到.下面给出一些比较合适的链接 http://developer.nvidia.com/object/cube_map_ogl_tutorial.html    NVIDIA官网上的 Opengl Cube texture mappinghttp://www.zwqxin.com/archives/shaderglsl/review-cube-mapping-shader.html  某位兄弟的个人BLOG. 以上两位都适合OPENGL控. 本文给出一个DX HLSL例子

JavaScript链式结构序列化详解

一.概述 在JavaScript中,链式模式代码,太多太多,如下: if_else: if(...){      //TODO  }else if(...){      //TODO  }else{      //TODO  }  switch: switch(name){      case ...:{          //TODO          break;      }      case ...:{          //TODO          break;      }   

c++显式类型转换示例详解_C 语言

标准C++包含一个显式的转换语法: static_cast:用于"良性"和"适度良性"的转换,包括不用强制转换 const_cast:用于"const"和/或"volatile"进行转换 reinterpret_cast:转换为完全不同的意思.为了安全的使用它,关键必须转换回原来的类型.转换成的类型一般只能用于位操作,否则就是为了其他隐秘的目的.这是所有转换中最危险的. dynamic_cast:用于类型安全的向下转换 ---

JavaScript 链式结构序列化详解_基础知识

一.概述 在JavaScript中,链式模式代码,太多太多,如下: if_else: if(...){ //TODO }else if(...){ //TODO }else{ //TODO } switch: switch(name){ case ...:{ //TODO break; } case ...:{ //TODO break; } default:{ //TODO } } 疑问:诸如上述这些链式代码,倘若,我们想将其扁平化链式处理呢?如下: //fn1,f2,f3为处理函数 _if(

张何:什么是软文 软文广告发布与推广详解

中介交易 SEO诊断 淘宝客 云主机 技术大厅 什么是软文 顾名思义,它是相对于硬性广告而言,由企业的市场策划人员或广告公司的文案人员来负责撰写的"文字广告".与硬广告相比,软文之所以叫做软文,精妙之处就在于一个"软"字,好似绵里藏针,收而不露,克敌于无形,等到你发现这是一篇软文的时候,你已经冷不盯的掉入了被精心设计过的"软文广告"陷阱.它追求的是一种春风化雨.润物无声的传播效果.如果说硬广告是外家的少林功夫;那么,软文则是绵里藏针.以柔克刚的武

详解Clojure的递归(上)—— 直接递归及优化

    详解clojure递归(上)     详解clojure递归(下)     递归可以说是LISP的灵魂之一,通过递归可以简洁地描述数学公式.函数调用,Clojure是LISP的方言,同样需要递归来扮演重要作用.递归的价值在于可以让你的思维以what的形式思考,而无需考虑how,你写出来的代码就是数学公式,就是函数的描述,一切显得直观和透明.如果你不习惯递归,那只是因为命令式语言的思维根深蒂固,如x=x+1这样的表达式,从数学的角度来看完全不合法,但是在命令式语言里却是合法的赋值语句.