一、时间复杂度
通常,对于一个给定的算法,我们要做两项分析:第一步是从数学上证明算法的正确性。第二步就是分析算法的时间复杂度。
1.时间频度
一个算法耗费的时间在理论上是无法算出来的,只有在机器上实际运行过才能知道,但这样太过麻烦,所以我们需要使用一个计算方法大概的计算出它所耗费的时间。
释义:一个算法中的语句执行次数称为语句频度或时间频度。记为T(n),n为语句执行次数。
2.时间复杂度
在刚才提到的时间频度中,n是语句执行次数,也就是问题的规模,当n不断变化时,时间频度T(n)也会不断变化。一般情况下,影响问题规模的最大因素就是循环。
释义:若某个循环(for、while或递归)的语句频度f(n),使得当n趋近于无穷大时,T(n)/f(n)的极限值为不等于零的常数,则称f(n)是T(n)的同数量级函数。记作T(n)=O(f(n)),称O(f(n)) 为算法的渐进时间复杂度,简称时间复杂度。
T (n) = Ο(f (n)) 表示存在一个常数C,使得在当n趋于 正无穷时总有 T (n) ≤ C * f(n)。简单来说,就是T(n)在n趋于正无穷时最大也就跟f(n)差不多大。也就是说当n趋于正无穷时T (n)的上界是C * f(n)。其虽然对f(n)没有规定,但是一般都是取尽可能简单的函数。例如,O(2n2+n +1) = O (3n2+n+3) = O (7n2 + n) = O ( n2 ) ,一般都只用O(n2)表示就可以了。注意到大O符号里隐藏着一个常数C,所以f(n)里一般不加系数。如果把T(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的不断增大,上述时间复杂度不断增大,算法的执行效率越低。
常见的算法时间复杂度由小到大依次为:Ο(1)<Ο(log2n)<Ο(n)<Ο(nlog2n)<Ο(n2) <Ο(n3)<…<Ο(2n)<O(3n)<…<Ο(n!)
如果一个算法时间复杂度是O(1)、Ο(log2n)、Ο(n)、Ο(nlog2n)时,那么算法的效率是比较高的;Ο(n2)、Ο(n3)、……差强人意;Ο(2n)、……、Ο(n!)则是很差的算法,如果n大一些计算机就很难计算了。
例题:
sum=0; (1次)
for(i=1;i<=n;i++) (n次)
{
for(j=1;j<=n;j++) (n^2次)
{
sum++; (n^2次)
}
}
时间复杂度:O(1+n+n2+n2)=O(2n2+n+1)=O(n2)
二、空间复杂度
一般情况下,算法的空间复杂度都是很低的,算法的潮流也是追求时间复杂度越小越好。
1.空间复杂度
释义:S(n)为该算法所耗费的总的存储空间,问题规模最大耗费空间最多的那个问题所耗费的空间为f(n);而空间复杂度是对一个算法在运行过程中临时占用存储空间大小的量度,记做S(n)=O(f(n))。
空间复杂度(Space Complexity)是对一个算法在运行过程中临时占用存储空间大小的量度。一个算法在计算机存储器上所占用的存储空间,包括存储算法本身所占用的存储空间,算法的输入输出数据所占用的存储空间和算法在运行过程中临时占用的存储空间这三个方面。算法的输入输出数据所占用的存储空间是由要解决的问题决定的,是通过参数表由调用函数传递而来的,它不随本算法的不同而改变。存储算法本身所占用的存储空间与算法书写的长短成正比,要压缩这方面的存储空间,就必须编写出较短的算法。算法在运行过程中临时占用的存储空间随算法的不同而异,有的算法只需要占用少量的临时工作单元,而且不随问题规模的大小而改变,我们称这种算法是“就地"进行的,是节省存储的算法,如这一节介绍过的几个算法都是如此;有的算法需要占用的临时工作单元数与解决问题的规模n有关,它随着n的增大而增大,当n较大时,将占用较多的存储单元,例如将在第九章介绍的快速排序和归并排序算法就属于这种情况。
如当一个算法的空间复杂度为一个常量,即不随被处理数据量n的大小而改变时,可表示为O(1);当一个算法的空间复杂度与以2为底的n的对数成正比时,可表示为0(10g2n);当一个算法的空I司复杂度与n成线性比例关系时,可表示为0(n).若形参为数组,则只需要为它分配一个存储由实参传送来的一个地址指针的空间,即一个机器字长空间;若形参为引用方式,则也只需要为其分配存储一个地址的空间,用它来存储对应实参变量的地址,以便由系统自动引用实参变量。
例题:
(1)
1 int fun(int n){
2 int i,j,k,s;
3 s=0;
4 for (i=0;i<=n;i++)
5 {
6 for (j=0;j<=i;j++)
7 {
8 for (k=0;k<=j;k++)
9 {
10 s++;
11 }
12 }
13 }
14 return s;
15 }
由于算法中临时变量得个数与问题规模n无关,所以空间复杂度均为:
S(n) = O(1)
(2)
1 void fun(int a[],int n,int k)
2 //数组a共有n个元素
3 {
4 int i;
5 if (k==n-1)
6 {
7 for (i=0;i<n;i++)
8 {
9 printf(“%d\n”,a[i]); //执行n次
10 }
11 }
12 else
13 {
14 for (i=k;i<n;i++)
15 {
16 a[i]=a[i]+i*i; //执行n-k次
17 }
18 fun(a,n,k+1);
19 }
20 }
S(n) = O(g(1*n))
此方法属于递归算法,每次调用本身都要分配空间,fun(a,n,0)的空间复杂度为O(n)。