1.算法设计与分析概述 在总结递归算法的时间复杂度分析之前,应该明确几组概念。 算法仅仅是求解问题的解决方案,这个解决方案本身并不是问题的答案,而是能获得答案的指令序列。只有通过执行算法才可以获得求解问题的答案。 从算法是否递归调用的角度看,算法可以分为非递归算法和递归算法。 非递归算法时间复杂度分析较为简单,通常是计算算法中基本语句执行次数,一般都是一个关于问题规模n的表达式,然后用渐近符号Θ、Ο、o、Ω、ω表示出算法的时间复杂度。 递归算法是采用分治的方法,把一个“大问题”分解出若干个相似的“小问题”求解。在分析算法复杂度时,关键是根据递归过程建立递推关系式,然后求解递推关系式,得到算法执行的时间表达式(一般都与问题规模n相关),最后用渐近符号Θ、Ο、o、Ω、ω表示出算法的时间复杂度。 在《算法导论》、《算法设计与分析》这2门课中,我们已经学习一些通用的算法设计技术,如增量法、分治法、贪心法、动态规划、线性规划、回溯法、分支限界法等;在算法设计完成后,对算法的复杂度进行分析是必然的,所以本篇的中心将围绕算法时间复杂度展开。 2.非递归算法分析 例1:如果算法的执行时间不随着问题规模n的增加而增长,它的基本语句执行的次数是固定的,总的时间由一个常数来限界。此类算法的时间复杂度是O(1)。
例2:当有若干个循环语句时,时间复杂度是由嵌套层数最多的循环语句中的基本语句的执行次数决定。如下 void fun(int n){ int x=0; for(int i=1;i<=n;i++){ for(int j=1;j<=i;j++){ for(int k=1;k<=j;k++){ x++; //基本语句 } } } } 解:该算法的基本语句是x++;所以 虽然非递归算法的时间复杂度比较好分析,但往往需要用到多项式的求和技巧和放缩技巧,如: 小结:以上这些求和及放缩技巧在下面的递归树求总代价、主定理证明过程中也要用到,非常的重要! 3.递归算法分析 3.1利用数列知识 忽略求解细节。在我们求解递归式时,因为最终是要求得一个时间上限,所以在求解时常常省略一些细节。比如mergeSort(a,0,n-1)运行时间的实际递归式应该是: 但我们忽略这些上取整、下取整以及边界条件,甚至假设问题规模 n=2k,这都是为方便求解而忽略的细节。经验和一些定理告诉我们,这些细节不会影响算法时间复杂度的渐近界。 类似的,我们也可以用迭代法求解汉诺塔递归求解时的时间复杂度。但遗憾的是,迭代法一般适用于一阶的递推方程。对于二阶及以上(即T(n)依赖它前面更多个递归项T(n)依赖它前面更多个递归项)的递推方程,迭代法将导致迭代后的项太多,从而使得求和公式过于复杂,因此需要将递推方程化简,利用差消法等技巧将高阶递推方程化为一阶递推方程。如在求快速排序算法平均时间复杂度T(n)的递推方程,T(n)依赖T(n−1)、T(n−2)、...、T(1)等所有的项,这样的递推方程也称为全部历史递推方程。(这里省略快速排序算法平均复杂度T(n)的求解过程) 小结:上面6种递推关系是高中、本科知识,在此重点介绍了迭代法,其它几种方法虽未在本篇中使用,但可以加深对递推式求解的认识。
|