算法的时间复杂度分析

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

算法的时间复杂度分析

1.算法耗费的时间、语句频度、输入规模
在实际中,一个算法所需耗费的时间 = 算法中所有语句的执行时间之和
而,
每条语句的执行时间= 每条语句执行一次所需时间 * 每条语句的执行次数(语句频度);

因为每条语句执行一次所需时间取决于机器执行指令的性能、速度等难以确定的因素,而为了独立于机器的软、硬件系统来分析算法的时间耗费,
我们假设每条语句执行一次所需时间均为单位时间,那么,一个算法的时间规模就与其所有语句的频度之和相关。

输入规模:算法求解问题的输入量称为问题的规模,一般用一整数来表示:n = 1, 2, 3 …k

那么,对于输入规模为 n 的算法,其语句频度之和可记作:T(n)
例:
(1) for(i=0; i<n;j++) n+1
(2) for (j=0;j<n;j++) n(n+1)
(3) C[i][j]=0; n^2
(4) for (k=0; k<n; k++) n^2(n+1)
(5) C[i][j]=C[i][j]+A[i][k]*B[k][j]; n^3
分析:
语句(1)的循环控制变量i要增加到n,测试到i=n成立才会终止。故它的频度是n+1。但是它的循环体却只能执行n次。
语句(2)作为语句(1)循环体内的语句应该执行n次,但语句(2)本身要执行n+1次,所以语句(2)的频度是n(n+1)。
同理可得语句(3),(4)和(5)的频度分别是n^2,n^2(n+1)和n^3。
该算法中所有语句的频度之和(即算法的时间耗费)为: T(n)=2n^3+3n^2+2n+1 (1.1)
2. 时间复杂度
概念:
如某算法的语句频度之和为 T(n),那么,当 n 趋于无穷大虹,若存在函数 f(n)使得 T(n)/f(n)的极限值为不等于0的常数, 则称: f(n) 是 T(n) 的同数量级函数,记作:T(n) = O(f(n)) 称: O(f(n)) 为该算法的渐进时间复杂度,简称时间复杂度。
举例说明时间复杂度的求法:
交换i和j的内容。
Temp=i;
i=j;
j=temp;
以上三条单个语句的频度均为1,该程序段的执行时间是一个与问题规模n无关的常数。算法的时间复杂度为常数阶,记作T(n)=O(1)。
注意: 如果算法的执行时间不随着问题规模n的增加而增长,即使算法中有上千条语句,其执行时间也不过是一个较大的常数。此类算法的时间复杂度是O(1)。

变量计数之一:
(1) x=0;y=0;
(2) for(k-1;k<=n;k++)
(3) x++;
(4) for(i=1;i<=n;i++)
(5) for(j=1;j<=n;j++)
(6) y++;
一般情况下,对步进循环语句只需考虑循环体中语句的执行次数,忽略该语句中步长加1、终值判别、控制转移等成分。
因此,以上程序段中频度最大的语句是(6),其频度为f(n)=n^2,所以该程序段的时间复杂度为T(n)=O(n^2)。
当有若干个循环语句时,算法的时间复杂度是由嵌套层数最多的循环语句中最内层语句的频度f(n)决定的。
变量计数之二:
(1) x=1;
(2) for(i=1;i<=n;i++)
(3) for(j=1;j<=i;j++)
(4) for(k=1;k<=j;k++)
(5) x++;
该程序段中频度最大的语句是(5),内循环的执行次数虽然与问题规模n没有直接关系,但是却与外层循环的变量取值有关,而最外层循环的次数直接与n有关,
因此可以从内层循环向外层分析语句(5)的执行次数n^3,得出该算法时间复杂度为T(n)=O(n^3/6+低次项)=O(n^3)。
注意: 算法的时间复杂度不仅仅依赖于问题的规模,还与输入实例的初始状态有关。
在数值A[0..n-1]中查找给定值K的算法大致如下:
(1)i=n-1;
(2)while(i>=0&&(A[i]!=k))
(3) i–;
(4)return i;
此算法中的语句(3)的频度不仅与问题规模n有关,还与输入实例中A的各元素取值及K的取值有关:
①若A中没有与K相等的元素,则语句(3)的频度f(n)=n;
②若A的最后一个元素等于K,则语句(3)的频度f(n)是常数0。

3.最坏时间复杂度和平均时间复杂度
最坏情况下的时间复杂度称最坏时间复杂度。一般不特别说明,讨论的时间复杂度均是最坏情况下的时间复杂度。
这样做的原因是:最坏情况下的时间复杂度是算法在任何输入实例上运行时间的上界,这就保证了算法的运行时间不会比任何更长。
平均时间复杂度是指所有可能的输入实例均以等概率出现的情况下,算法的期望运行时间。

递归函数时间复杂度分析

由于递归式复杂度的难以确定,所以目前常用的方法有这么几种:代换猜测法、递归树法、主定理、直接数学分析法
代换猜测法通常和递归树法合用,利用递归树法得到一个大概正确的结果,然后利用数学归纳法对其验证。直接的数学分析法相对很直接,很强大,但是对数学要求很高,尤其是碰到一些BT的表达式。

递归算法的分析方法比较多,最常用的便是迭代法。
迭代法的基本步骤是先将递归算法简化为对应的递归方程,然后通过反复迭代,将递归方程的右端变换成一个级数,最后求级数的和,再估计和的渐进阶。
<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=2的k次方。
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))2的i次方
= 2的(k-1)次方 + (2的k次方) - 2
= (3/2) * (2的k次方) - 2
= (3/2) * n - 2
= O(n)
这个例子的时间复杂性也是线性的。
<3> 例:如下递归方程:
T(n) = 2T(n/2) + O(n), 且假设n=2的k次方。
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)
由此表明,对于第二种递归形式调用,借助于递归树,用迭代法进行算法分析是简单易行的。

此外,主定理是最常用的方法,主定理通常可以解决如下的递归表达式:

T(n)=aT(n/b)+f(n)

上面的递归式描述的是将规模为n的问题划分为a个子问题,并且每个子问题的规模是n/b,这里a和b是正常数。划分原问题和合并结果的代价有函数f(n)描述。

主定理有三种情况,不同的情况有不同的用法:
算法的时间复杂度分析_第1张图片

对于应用主定理来说,一定要分清选取定定理中的哪种情况(如果有符合的),我们针对每一种类型,一一尝试下:
算法的时间复杂度分析_第2张图片

在某些情况下,存在一些特殊情况,比如明显不满足主定理形式,但是经过变形之后可以应用主定理。甚至在某些情况下,看上去符合主定理的递归式是无法应用主定理求解的,因为主定理不覆盖所有情况,即递归式不满足上面三条中的任意一条,我们逐一来看看这些特殊情况:

这里写图片描述

这种形式不符合主定理,但是可以简单的通过递归树加上一点点的数学分析可知,总共有n层,每层都是n的代价,所以总代价应该是O(n2)。
这里写图片描述
n带了根号,,但是可以通过换元的技巧将递归式转化成可以用主定理求解的式子。
算法的时间复杂度分析_第3张图片
然而对于某些式子,如上面所说,看着似乎可以,但是实际上不满足3条中的任何一条,比如下面的:
这里写图片描述
算法的时间复杂度分析_第4张图片

经常碰到的算法中,很多都是基于递归的,比如快速排序、归并排序、二分查找,求Fibonaci数列的递归等,一些常见的时间复杂度按数量级递增排列依次为:
常数阶 O(1)
对数阶 O(log2n)
线性阶 O(n)
线性对数阶 O(nlog2n)
平方阶 O(n^2)
立方阶 O(n^3)

k次方阶 O(n^k)
指数阶 O(2^n)

你可能感兴趣的:(Algorithms)