时间和空间复杂度
本文主要介绍 时间和空间复杂的概念和如何计算
时间复杂度介绍
-
时间频度
一个算法执行所耗费的时间,从理论上是不能算出来的,必须上机运行测试才能知道。但我们不可能也没有必要对每个算法都上机测试,只需知道哪个算法花费的时间多,哪个算法花费的时间少就可以了。并且一个算法花费的时间与算法中语句的执行次数成正比例,哪个算法中语句执行次数多,它花费时间就多。一个算法中的语句执行次数称为语句频度或时间频度。记为T(n)。
-
时间复杂度
在刚才提到的时间频度中,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)) 为算法的渐进时间复杂度,简称时间复杂度。
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 ( 2 n 2 + n + 1 ) = O ( 3 n 2 + n + 3 ) = O ( 7 n 2 + n ) = O ( n 2 ) O(2 n^2+n +1) = O (3n^2+n+3) = O (7n^2 + n) = O ( n^2 ) O(2n2+n+1)=O(3n2+n+3)=O(7n2+n)=O(n2) ,一般都只用 O ( n 2 ) O(n^2) O(n2) 表示就可以了。注意到大O符号里隐藏着一个常数C,所以f(n)里一般不加系数。如果把T(n)当做一棵树,那么O(f(n))所表达的就是树干,只关心其中的主干,其他的细枝末节全都抛弃不管。
在各种不同算法中,若算法中语句执行次数为一个常数,则时间复杂度为O(1),另外,在时间频度不相同时,时间复杂度有可能相同,如 T ( n ) = n 2 + 3 n + 4 与 T ( n ) = 4 n 2 + 2 n + 1 T(n)=n^2+3n+4与T(n)=4n^2+2n+1 T(n)=n2+3n+4与T(n)=4n2+2n+1 它们的频度不同,但时间复杂度相同,都为 O ( n 2 ) O(n^2) O(n2) 。按数量级递增排列,常见的时间复杂度有:常数阶O(1),对数阶 O ( l o g 2 n ) O(log_2n) O(log2n),线性阶O(n), 线性对数阶 O ( n l o g 2 n ) O(nlog_2n) O(nlog2n),平方阶 O ( n 2 ) O(n^2) O(n2),立方阶&O(n^3) , k 次 方 阶 , k次方阶 ,k次方阶O(n^k)$,指数阶O(2的n次方) 。随着问题规模n的不断增大,上述时间复杂度不断增大,算法的执行效率越低。
从图中可见,我们应该尽可能选用多项式阶O(n^k)的算法,而不希望用指数阶的算法。
- 常见的算法时间复杂度由小到大依次为: O ( 1 ) < O ( l o g 2 n ) < O ( n ) < O ( n l o g 2 n ) < O ( n 2 ) < O ( n 3 ) < … < O ( 2 的 n 次 方 ) < O ( n ! ) Ο(1)<Ο(log_2n)<Ο(n)<Ο(nlog_2n)<Ο(n^2)<Ο(n^3)<…<Ο(2的n次方)<Ο(n!) O(1)<O(log2n)<O(n)<O(nlog2n)<O(n2)<O(n3)<…<O(2的n次方)<O(n!)
时间复杂度计算
1.找出算法中的基本语句;
算法中执行次数最多的那条语句就是基本语句,通常是最内层循环的循环体。
2.计算基本语句的执行次数的数量级
只需计算基本语句执行次数的数量级,这就意味着只要保证基本语句执行次数的函数中的最高次幂正确即可,可以忽略所有低次幂和最高次幂的系数。这样能够简化算法分析,并且使注意力集中在最重要的一点上:增长率。
3.用大Ο记号表示算法的时间性能
将基本语句执行次数的数量级放入大Ο记号中
例子:
for (i=1; i<=n; i++)
x++;
for (i=1; i<=n; i++)
for (j=1; j<=n; j++)
x++;
第一个for循环的时间复杂度为Ο(n),第二个for循环的时间复杂度为 O ( n 2 ) Ο(n^2) O(n2),则整个算法的时间复杂度为 O ( n + n 2 ) = O ( n 2 ) Ο(n+n^2)=Ο(n^2) O(n+n2)=O(n2)。
Ο(1)表示基本语句的执行次数是一个常数,一般来说,只要算法中不存在循环语句,其时间复杂度就是Ο(1)
例子2:
sum=0; (一次)
for(i=1;i<=n;i++) (n+1次)
for(j=1;j<=n;j++) (n2次)
sum++; (n2次)
因为 O ( 1 + n + 1 + n 2 + n 2 ) O(1+n+1 +n^2 + n^2) O(1+n+1+n2+n2)= O ( 2 n 2 + n + 2 ) O(2n^2+n+2) O(2n2+n+2) = n 2 n^2 n2(Θ即:去低阶项,去掉常数项,去掉高阶项的常参得到),所以T(n)= O ( n 2 ) O(n^2) O(n2);
空间复杂度介绍
空间复杂度是对一个算法在运行过程中临时占用存储空间大小的一个量度,同样反映的是一个趋势,我们用 S(n) 来定义。
空间复杂度比较常用的有:O(1)、O(n)、O(n²)
空间复杂度的计算
- 空间复杂度 O(1)
-
如果算法执行所需要的临时空间不随着某个变量n的大小而变化,即此算法空间复杂度为一个常量,可表示为 O(1)
举例:int i = 1;
int j = 2;
++i;
j++;
int m = i + j; -
代码中的 i、j、m 所分配的空间都不随着处理数据量变化,因此它的空间复杂度 S(n) = O(1)
- 空间复杂度 O(n)
int[] m = new int[n]
for(i=1; i<=n; ++i)
{
j = i;
j++;
}
这段代码中,第一行new了一个数组出来,这个数据占用的大小为n,这段代码的2-6行,虽然有循环,但没有再分配新的空间,因此,这段代码的空间复杂度主要看第一行即可,即 S(n) = O(n)