空间复杂度
空间复杂度是程序运行所以需要的额外消耗存储空间, 也用o() 来表示
比如插入排序的时间复杂度 是o(n^2), 空间复杂度是o(1)
而一般的递归算法就要有o(n) 的空间复杂度了, 因为每次递归都要存储返回信息
一个算法的优劣主要从算法的执行时间和所需要占用的存储空间两个方面衡量,算法执行时 间的度量不是采用算法执行的绝对时间来计算的,因为一个算法在不同的机器上执行所花的时间不一样,在不同时刻也会由于计算机资源占用情况的不同,使得算法 在同一台计算机上执行的时间也不一样,所以对于算法的时间复杂性 ,采用算法执行过程中其基本操作的执行次数,称为计算量来度量。
算法中基本操作的执行次数一般是与问题规模有关的,对于结点个数为n 的数据处理问题,用T(n) 表示算法基本操作的执行次数. 在评价算法的时间复杂性时,不考虑两算法执行次数之间的细小区别,而只关心算法的本质差别:
为此,引入一个所谓的O() 记号,则T1(n)=2n=O(n),T2(n)=n+1=O(n) 。一个函数f(n) 是O(g(n)) 的,则一定存在正常数c 和m ,使对所有的n>m ,都满足f(n)<c*g(n) 。
时间复杂度
同一问题可用不同算法 解决,而一个算法的质量优劣将影响到算法乃至程序 的效率。算法分析的目的在于选择合适算法和改进算法。
算法复杂度 分为时间复杂度和空间复杂度 。其作用:时间复杂度是度量算法执行的时间长短;而空间复杂度是度量算法所需存储空间的大小。
一个算法执行所耗费的时间,从理论上是不能算出来的,必须上机运行测试才能知道。但我 们不可能也没有必要对每个算法都上机测试,只需知道哪个算法花费的时间多,哪个算法花费的时间少就可以了。并且一个算法花费的时间与算法中语句的执行次数 成正比例,哪个算法中语句执行次数多,它花费时间就多。一个算法中的语句执行次数称为语句频度或时间频度。记为T(n) 。
1. 一般情况下,算法的基本操作重复执行的次数是模块n 的某一个函数f (n ),因此,算法的时间复杂度记做:T (n )=O (f (n ))
分析:随着模块n 的增大,算法执行的时间的增长率和f (n )的增长率成正比,所以f (n )越小,算法的时间复杂度越低,算法的效率越高。
2. 在计算时间复杂度的时候,先找出算法的基本操作,然后根据相应的各语句确定它的执行次数,再找出T (n )的同数量级(它的同数量级有以下:1 ,Log2n ,n ,nLog2n ,n 的平方,n 的三次方,2 的n 次方,n !),找出后,f (n )= 该数量级,若T(n)/f(n) 求极限可得到一常数c ,则时间复杂度T (n )=O (f (n ))
例:算法:
for (i=1;i<=n;++i )
{
for(j=1;j<=n;++j)
{
c[ i ][ j ]=0; // 该步骤属于基本操作执行次数:n 的平方 次
for(k=1;k<=n;++k)
c[ i ][ j ]+=a[ i ][ k ]*b[ k ][ j ]; // 该步骤属于基本操作 执行次数:n 的三次方 次
}
}
则有 T (n )= n 的平方+n 的三次方,根据上面括号里的同数量级,我们可以确定 n 的三次方 为T (n )的同数量级
则有f (n )= n 的三次方,然后根据T (n )/f (n )求极限可得到常数c
则该算法的 时间复杂度:T (n )=O (n 的三次方)
按数量级递增排列,常见的时间复杂度有:
常数阶O(1), 对数阶O(log2n), 线性阶O(n),
线性对数阶O(nlog2n), 平方阶O(n2) ,立方阶O(n3),... ,
k 次方阶O(nk), 指数阶O(2n) 。随着问题规模n 的不断增大,上述时间复杂度不断增大,算法的执行效率越低。
与时间复杂度类似,空间复杂度是指算法在计算机内执行时所需存储空间的度量。记作:
S(n)=O(f(n))
我们一般所讨论的是除正常占用内存开销外的辅助存储单元规模。
算法的时间复杂度(计算实例)
算法的时间复杂度
定义:如果一个问题的规模是n ,解这一问题的某一算法所需要的时间为T(n) ,它是n 的某一函数 T(n) 称为这一算法的“时间复杂性”。
当输入量n 逐渐加大时,时间复杂性的极限情形称为算法的“渐近时间复杂性”。
我们常用大O 表示法表示时间复杂性,注意它是某一个算法的时间复杂性。大O 表示只是说有上界,由定义如果f(n)=O(n) ,那显然成立f(n)=O(n^2) ,它给你一个上界,但并不是上确界,但人们在表示的时候一般都习惯表示前者。
此外,一个问题本身也有它的复杂性,如果某个算法的复杂性到达了这个问题复杂性的下界,那就称这样的算法是最佳算法。
“大O 记法”:在这种描述中使用的基本参数是 n ,即问题实例的规模,把复杂性或运行时间表达为n 的函数。这里的“O ”表示量级 (order) ,比如说“二分检索是 O(logn) 的”, 也就是说它需要“通过logn 量级的步骤去检索一个规模为n 的数组”记法 O ( f(n) ) 表示当 n 增大时,运行时间至多将以正比于 f(n) 的速度增长。
这种渐进估计对算法的理论分析和大致比较是非常有价值的,但在实践中细节也可能造成差异。例如,一个低附加代价的O(n2) 算法在n 较小的情况下可能比一个高附加代价的 O(nlogn) 算法运行得更快。当然,随着n 足够大以后,具有较慢上升函数的算法必然工作得更快。
O(1)
Temp=i;i=j;j=temp;
以上三条单个语句的频度均为1 ,该程序段的执行时间是一个与问题规模n 无关的常数。算法的时间复杂度为常数阶,记作T(n)=O(1) 。如果算法的执行时间不随着问题规模n 的增加而增长,即使算法中有上千条语句,其执行时间也不过是一个较大的常数。此类算法的时间复杂度是O(1) 。
O(n^2)
2.1. 交换i 和j 的内容
sum=0 ; (一次)
for(i=1;i<=n;i++) (n 次)
for(j=1;j<=n;j++) (n^2 次)
sum++ ; (n^2 次)
解: T(n)=2n^2+n+1 =O(n^2)
2.2.
for (i=1;i<n;i++)
{
y=y+1; ①
for (j=0;j<=(2*n);j++)
x++; ②
}
解:语句1 的频度是 n-1
语句2 的频度是 (n-1)*(2n+1)=2n^2-n-1
f(n)=2n^2-n-1+(n-1)=2n^2-2
该程序的时间复杂度T(n)=O(n^2).
O(n)
2.3.
a=0;
b=1; ①
for (i=1;i<=n;i++) ②
{
s=a+b; ③
b=a; ④
a=s; ⑤
}
解:语句1 的频度:2,
语句2 的频度: n,
语句3 的频度: n-1,
语句4 的频度:n-1,
语句5 的频度:n-1,
T(n)=2+n+3(n-1)=4n-1=O(n).
O(log2n )
2.4.
i=1; ①
while (i<=n)
i=i*2; ②
解: 语句1 的频度是1,
设语句2 的频度是f(n), 则:2^f(n)<=n;f(n)<=log2n
取最大值 f(n)= log2n,
T(n)=O(log2n )
O(n^3)
2.5.
for(i=0;i<n;i++)
{
for(j=0;j<i;j++)
{
for(k=0;k<j;k++)
x=x+2;
}
}
解:当i=m, j=k 的时候, 内层循环的次数为k 当i=m 时, j 可以取 0,1,...,m-1 , 所以这里最内循环共进行了0+1+...+m-1=(m-1)m/2 次所以,i 从0 取到n, 则循环共进行了: 0+(1-1)*1/2+...+(n-1)n/2=n(n+1)(n-1)/6 所以时间复杂度为 O(n^3).
我们还应该区分算法的最坏情况的行为和期望行为。如快速排序的最坏情况运行时间是 O(n^2) ,但期望时间是 O(nlogn) 。通过每次都仔细地选择基准值,我们有可能把平方情况 ( 即O(n^2) 情况) 的概率减小到几乎等于 0 。在实际中,精心实现的快速排序一般都能以 (O(nlogn) 时间运行。
下面是一些常用的记法:
访问数组中的元素是常数时间操作,或说O(1) 操作。一个算法如 果能在每个步骤去掉一半数据元素,如二分检索,通常它就取 O(logn) 时间。用strcmp 比较两个具有n 个字符的串需要O(n) 时间。常规的矩阵乘算法是O(n^3) ,因为算出每个元素都需要将n 对 元素相乘并加到一起,所有元素的个数是n^2 。
指数时间算法通常来源于需要求出所有可能结果。例如,n 个元 素的集合共有2n 个子集, 所以要求出所有子集的算法将是O(2n) 的。指数算法一般说来是太复杂了,除非n 的值非常小,因为,在 这个问题中增加一个元素就导致运行时间加倍。不幸的是,确实有许多问题 ( 如著名的“巡回售货员问题” ) ,到目前为止找到的算法都是指数的。如果我们真的遇到这种情况,通常应该用寻找近似最佳结果的算法替代之。
时间复杂度O(n) 什么意思
时间复杂度
算法分析
同一问题可用不同算法解决,而一个算法的质量优劣将影响到算法乃至程序的效率。算法分析的目的在于选择合适算法和改进算法。一个算法的评价主要从时间复杂度和空间复杂度来考虑。
1 、时间复杂度
(1 )时间频度
一个算法执行所耗费的时间,从理论上是不能算出来的,必须上机运行 测试才能知道。但我们不可能也没有必要对每个算法都上机测试,只需知道哪个算法花费的时间多,哪个算法花费的时间少就可以了。并且一个算法花费的时间与算 法中语句的执行次数成正比例,哪个算法中语句执行次数多,它花费时间就多。一个算法中的语句执行次数称为语句频度或时间频度。记为T(n) 。
(2 )时间复杂度
在刚才提到的时间频度中,n 称为问题的规模,当n 不断变化时,时间频度T(n) 也会不断变化。但有时我们想知道它变化时呈现什么规律。为此,我们引入时间复杂度概念。
一般情况下,算法中基本操作重复执行的次数是问题规模n 的某个函数,用T(n) 表示,若有某个辅助函数f(n), 使得当n 趋近于无穷大时,T (n)/f(n) 的极限值为不等于零的常数,则称f(n) 是T(n) 的同数量级函数。记作T(n)=O(f(n)), 称O(f(n)) 为算法的渐进时间复杂度,简称时间复杂度。
在各种不同算法中,若算法中语句执行次数为一个常数,则时间复杂度为O(1), 另外,在时间频度不相同时,时间复杂度有可能相同,如T(n)=n2+3n+4 与T(n)=4n2+2n+1 它们的频度不同,但时间复杂度相同,都为O(n2) 。
按数量级递增排列,常见的时间复杂度有:
常数阶O(1), 对数阶O(log2n), 线性阶 O(n),
线性对数阶O(nlog2n), 平方阶O(n2) ,立方阶O(n3),... ,
k 次方阶O(nk), 指数阶O(2n) 。随着问题规模n 的不断增大,上述时间复杂度不断增大,算法的执行效率越低。
2 、空间复杂度
与时间复杂度类似,空间复杂度是指算法在计算机内执行时所需存储空间的度量。记作 :
S(n)=O(f(n))
我们一般所讨论的是除正常占用内存开销外的辅助存储单元规模。
参考资料:http://bk.baidu.com/lemma-php/dispose/view.php/104946.htm
常见算法时间复杂度:
O(1): 表示算法的运行时间为常量
O(n): 表示该算法是线性算法
O( ㏒2 n): 二分查找算法
O(n2 ): 对数组进行排序的各种简单算法,例如直接插入排序的算法。
O(n3 ): 做两个n 阶矩阵的乘法运算
O(2n ): 求具有n 个元素集合的所有子集的算法
O(n!): 求具有N 个元素的全排列的算法
优<---------------------------< 劣
O(1)<O( ㏒2 n)<O(n)<O(n2 )<O(2n )
时间复杂度按数量级递增排列依次为:常数阶O(1) 、对数阶O(log2 n) 、线性阶O(n) 、线性对数阶O(nlog2 n) 、平方阶O(n2 ) 、立方阶O(n3 ) 、……k 次方阶O(nk ) 、指数阶O(2n ) 。
时间复杂度和空间复杂度
(2007-03-30 22:13)
同一问题可用不同算法解决,而一个算法的质量优劣将影响到算法乃至程序的效率。算法分析的目的在于选择合适算法和改进算法。一个算法的评价主要从时间复杂度和空间复杂度来考虑。
1 、时间复杂度
(1 )时间频度
一 个算法执行所耗费的时间,从理论上是不能算出来的,必须上机运行测试才能知道。但我们不可能也没有必要对每个算法都上机测试,只需知道哪个算法花费的时间 多,哪个算法花费的时间少就可以了。并且一个算法花费的时间与算法中语句的执行次数成正比例,哪个算法中语句执行次数多,它花费时间就多。一个算法中的语 句执行次数称为语句频度或时间频度。记为T(n) 。
(2 )时间复杂度
在刚才提到的时间频度中,n 称为问题的规模,当n 不断变化时,时间频度T(n) 也会不断变化。但有时我们想知道它变化时呈现什么规律。为此,我们引入时间复杂度概念。
一般情况下,算法中基本操作重复执行的次数是问题规模n 的某个函数,用T(n) 表示,若有某个辅助函数f(n), 使得当n 趋近于无穷大时,T (n)/f(n) 的极限值为不等于零的常数,则称f(n) 是T(n) 的同数量级函数。记作T(n)= O(f(n)), 称O(f(n)) 为算法的渐进时间复杂度,简称时间复杂度。
在各种不同算法中,若算法中语句执行次数为一个常数,则时间复杂度为O(1), 另外,在时间频度不相同时,时间复杂度有可能相同,如T(n)=n2+3n+4 与T(n)=4n2+2n+1 它们的频度不同,但时间复杂度相同,都为O(n2) 。
按数量级递增排列,常见的时间复杂度有:
常数阶O(1), 对数阶O(log2n), 线性阶 O(n),
线性对数阶O(nlog2n), 平方阶O(n2) ,立方阶O(n3),... ,
k 次方阶O(nk), 指数阶O(2n) 。随着问题规模n 的不断增大,上述时间复杂度不断增大,算法的执行效率越低。
2 、空间复杂度
与时间复杂度类似,空间复杂度是指算法在计算机内执行时所需存储空间的度量。记作 :
S(n)=O(f(n))
我们一般所讨论的是除正常占用内存开销外的辅助存储单元规模。讨论方法与时间复杂度类似,不再赘述。
(3 )渐进时间复杂度评价算法时间性能
主要用算法时间复杂度的数量级( 即算法的渐近时间复杂度) 评价一个算法的时间性能。
【例3 .7 】有两个算法A1 和A2 求解同一问题,时间复杂度分别是T1(n)=100n2 ,T2(n)=5n3 。
(1 )当输入量n <20 时,有T1(n) >T2(n) ,后者花费的时间较少。
(2 )随着问题规模n 的增大,两个算法的时间开销之比5n3/100n2=n/20 亦随着增大。即当问题规模较大时,算法A1 比算法A2 要有效地多。
它们的渐近时间复杂度O(n2) 和O(n3) 从宏观上评价了这两个算法在时间方面的质量。在算法分析时,往往对算法的时间复杂度和渐近时间复杂度不予区分,而经常是将渐近时间复杂度T(n)=O(f(n)) 简称为时间复杂度,其中的f(n) 一般是算法中频度最大的语句频度。
【例3 .8 】算法MatrixMultiply 的时间复杂度一般为T(n)=O(n3) ,f(n)=n3 是该算法中语句(5) 的频度。下面再举例说明如何求算法的时间复杂度。
【例3 .9 】交换i 和j 的内容。
Temp=i;
i=j;
j=temp;
以上三条单个语句的频度均为1 ,该程序段的执行时间是一个与问题规模n 无关的常数。算法的时间复杂度为常数阶,记作T(n)=O(1) 。
如果算法的执行时间不随着问题规模n 的增加而增长,即使算法中有上千条语句,其执行时间也不过是一个较大的常数。此类算法的时间复杂度是O(1) 。
【例3 .10 】变量计数之一。
(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)=n2 ,所以该程序段的时间复杂度为T(n)=O(n2) 。
当有若干个循环语句时,算法的时间复杂度是由嵌套层数最多的循环语句中最内层语句的频度f(n) 决定的。
【例3 .11 】变量计数之二。
(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) 的执行次数:
500){this.resized=true;this.style.width=500;}" v:shapes="_x0000_i1025">
则该程序段的时间复杂度为T(n)=O(n3/6+ 低次项)=O(n3) 。
(4 )算法的时间复杂度不仅仅依赖于问题的规模,还与输入实例的初始状态有关。
【例3 .12 】在数值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 。
(5 )最坏时间复杂度和平均时间复杂度
最坏情况下的时间复杂度称最坏时间复杂度。一般不特别说明,讨论的时间复杂度均是最坏情况下的时间复杂度。
这样做的原因是:最坏情况下的时间复杂度是算法在任何输入实例上运行时间的上界,这就保证了算法的运行时间不会比任何更长。
【例3 .19 】查找算法【例1 ·8 】在最坏情况下的时间复杂度为T(n)=0(n) ,它表示对于任何输入实例, 该算法的运行时间不可能大于0(n) 。
平均时间复杂度是指所有可能的输入实例均以等概率出现的情况下,算法的期望运行时间。
常见的时间复杂度按数量级递增排列依次为:常数0(1) 、对数阶0(log2n) 、线形阶0(n) 、线形对数阶0(nlog2n) 、平方阶0(n2) 立方阶0(n3) 、…、k 次方阶0(nk) 、指数阶0(2n) 。显然,时间复杂度为指数阶0(2n) 的算法效率极低,当n 值稍大时就无法应用。
类似于时间复杂度的讨论,一个算法的空间复杂度(Space Complexity)S(n) 定义为该算法所耗费的存储空间,它也是问题规模n 的函数。渐近空间复杂度也常常简称为空间复杂度。算法的时间复杂度和空间复杂度合称为算法的复杂度。
关键字: 算法复杂度 时间复杂度 空间复杂度
1 、时间复杂度
(1 ) 时间频度一个算法执行所耗费的时间,从理论上是不能算出来的,必须上机运行测试才能知道。但我们不可能也没有必要对每个算法都上机测试,只需知道哪个算法 花费的时间多,哪个算法花费的时间少就可以了。并且一个算法花费的时间与算法中语句的执行次数成正比例,哪个算法中语句执行次数多,它花费时间就多。一个 算法中的语句执行次数称为语句频度或时间频度。记为T(n) 。
(2 )时间复杂度在刚才提到的时间频度中,n 称为问题的规模,当n 不断变化时,时间频度T(n) 也会不断变化。但有时我们想知道它变化时呈现什么规律。为此,我们引入时间复杂度概念。一般情况下,算法中基本操作重复执行的次数是问题规模n 的某个函数,用T(n) 表示,若有某个辅助函数f(n), 使得当n 趋近于无穷大时,T (n)/f(n) 的极限值为不等于零的常数,则称f(n) 是T(n) 的同数量级函数。记作T(n)= O(f(n)), 称O(f(n)) 为算法的渐进时间复杂度,简称时间复杂度。
在各种不同算法中,若算法中语句执行次数为一个常数,则时间复杂度为O(1), 另外,在时间频度不相同时,时间复杂度有可能相同,如T(n)=n2+3n+4 与T(n)=4n2+2n+1 它们的频度不同,但时间复杂度相同,都为O(n2) 。 按数量级递增排列,常见的时间复杂度有:常数阶O(1), 对数阶O(log2n), 线性阶O(n), 线性对数阶O(nlog2n), 平方阶O(n2) ,立方阶O(n3),... , k 次方阶O(nk), 指数阶O(2n) 。随着问题规模n 的不断增大,上述时间复杂度不断增大,算法的执行效率越低。 2 、空间复杂度与时间复杂度类似,空间复杂度是指算法在计算机内执行时所需存储空间的度量。记作: S(n)=O(f(n)) 我们一般所讨论的是除正常占用内存开销外的辅助存储单元规模。讨论方法与时间复杂度类似,不再赘述。
(3 )渐进时间复杂度评价算法时间性能 主要用算法时间复杂度的数量级( 即算法的渐近时间复杂度) 评价一个算法的时间性能。
【例3 .7 】有两个算法A1 和A2 求解同一问题,时间复杂度分别是T1(n)=100n2 ,T2(n)=5n3 。
(1 )当输入量n <20 时,有T1(n) >T2(n) ,后者花费的时间较少。
(2 )随着问题规模n 的增大,两个算法的时间开销之比5n3/100n2=n/20 亦随着增大。即当问题规模较大时,算法A1 比算法A2 要有效地多。它们的渐近时间复杂度O(n2) 和O(n3) 从宏观上评价了这两个算法在时间方面的质量。在算法分析时,往往对算法的时间复杂度和渐近时间复杂度不予区分,而经常是将渐近时间复杂度T(n)=O(f(n)) 简称为时间复杂度,其中的f(n) 一般是算法中频度最大的语句频度。
【例3 .8 】算法MatrixMultiply 的时间复杂度一般为T(n)=O(n3) ,f(n)=n3 是该算法中语句(5) 的频度。下面再举例说明如何求算法的时间复杂度。
【例3 .9 】交换i 和j 的内容。
Temp=i; i=j; j=temp; 以上三条单个语句的频度均为1 ,该程序段的执行时间是一个与问题规模n 无关的常数。算法的时间复杂度为常数阶,记作T(n)=O(1) 。 如果算法的执行时间不随着问题规模n 的增加而增长,即使算法中有上千条语句,其执行时间也不过是一个较大的常数。此类算法的时间复杂度是O(1) 。
【例3 .10 】变量计数之一。
(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)=n2 ,所以该程序段的时间复杂度为T(n)=O(n2) 。 当有若干个循环语句时,算法的时间复杂度是由嵌套层数最多的循环语句中最内层语句的频度f(n) 决定的。
【例3 .11 】变量计数之二。
(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) 的执行次数: 则该程序段的时间复杂度为T(n)=O(n3/6+ 低次项)=O(n3) 。 (4 )算法的时间复杂度不仅仅依赖于问题的规模,还与输入实例的初始状态有关。
【例3 .12 】在数值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 。(5 )最坏时间复杂度和平均时间复杂度 最坏情况下的时间复杂度称最坏时间复杂度。一般不特别说明,讨论的时间复杂度均是最坏情况下的时间复杂度。 这样做的原因是:最坏情况下的时间复杂度是算法在任何输入实例上运行时间的上界,这就保证了算法的运行时间不会比任何更长。
【例3 .19 】查找算法
【例1 ·8 】在最坏情况下的时间复杂度为T(n)=0(n) ,它表示对于任何输入实例, 该算法的运行时间不可能大于0(n) 。
平均时间复杂度是指所有可能的输入实例均以等概率出现的情况下,算法的期望运行时间。
常见的时间复杂度按数量级递增排列依次为:常数0(1) 、对数阶0(log2n) 、线形阶0(n) 、线形对数阶0(nlog2n) 、平方阶0(n2) 立方阶0(n3) 、…、k 次方阶0(nk) 、指数阶0(2n) 。显然,时间复杂度为指数阶0(2n) 的算法效率极低,当n 值稍大时就无法应用。
2 、类似于时间复杂度的讨论,一个算法的空间复杂度(Space Complexity)S(n) 定义为该算法所耗费的存储空间,它也是问题规模n 的函数。渐近空间复杂度也常常简称为空间复杂度。
空间复杂度(Space Complexity) 是对一个算法在运行过程中临时占用存储空间大小的量度。一个算法在计算机存储器上所占用的存储空间,包括存储算法本 身所占用的存储空间,算法的输入输出数据所占用的存储空间和算法在运行过程中临时占用的存储空间这三个方面。算法的输入输出数据所占用的存储空间是由要解 决的问题决定的,是通过参数表由调用函数传递而来的,它不随本算法的不同而改变。存储算法本身所占用的存储空间与算法书写的长短成正比,要压缩这方面的存 储空间,就必须编写出较短的算法。算法在运行过程中临时占用的存储空间随算法的不同而异,有的算法只需要占用少量的临时工作单元,而且不随问题规模的大小 而改变,我们称这种算法是“就地/" 进行的,是节省存储的算法,如这一节介绍过的几个算法都是如此;有的算法需要占用的临时工作单元数与解决问题的规模n 有关,它随着n 的增大而增大,当n 较大时,将占用较多的存储单元,例如将在第九章介绍的快速排序和归并排序算法就属于这种情况。
分析一个算法所占用的存储空间要从各方面综合考虑。如对于递归算法来说,一般都比较简短, 算法本身所占用的存储空间较少,但运行时需要一个附加堆栈,从而占用较多的临时工作单元;若写成非递归算法,一般可能比较长,算法本身占用的存储空间较 多,但运行时将可能需要较少的存储单元。
一个算法的空间复杂度只考虑在运行过程中为局部变量分配的存储空间的大小,它包括为参数表 中形参变量分配的存储空间和为在函数体中定义的局部变量分配的存储空间两个部分。若一个算法为递归算法,其空间复杂度为递归所使用的堆栈空间的大小,它等 于一次调用所分配的临时存储空间的大小乘以被调用的次数( 即为递归调用的次数加1 ,这个1 表不开始进行的一次非递归调用) 。算法的空间复杂度一般也以数量级的形式给出。如当一个算法的空间复杂度为一个常量,即不随被处理数据量n 的大小而改变时,可表示为O(1) ;当一个算法的空间复杂度与以2 为底的n 的对数成正比时,可表示为0(10g2n) ;当一个算法的空I 司复杂度与n 成线性比例关系时,可表示为0(n). 若形参为数组,则只需要为它分配一个存储由实参传送来的一个地址指针的空间,即一个机器字长空间;若形参为引用方式,则也只需要为其分配存储一个地址的空间,用它来存储对应实参变量的地址,以便由系统自动引用实参变量。
对于一个算法,其时间复杂度和空间复杂度往往是相互影响的。当追求一个较好的时间复杂度时,可能会使空间复杂度的性能变差,即可能导致占用较多的存储空间;反之,当=i 自求一个较好的空间复杂度时,可能会使时间复杂度的性能变差,即可能导致占用较长的运行时间。另外,算法的所有性能之间都存在着或多或少的相互影响。因此,当设计一个算法( 特别是大型算法) 时,要综合考虑算法的各项性能,算法的使用频率,算法处理的数据量的大小,算法描述语言的特性,算法运行的机器系统环境等各方面因素,才能够设计出比较好的算法。
算法的时间复杂度和空间复杂度合称为算法的复杂度。