1.时间复杂度
(1)T(n)表示一个算法中的语句执行次数,称为语句频度,记为T(n)。
(2)一般情况下,算法中的基本操作语句的重复执行次数是问题规模n的某个函数,用T(n)表示,若有某个辅助函数f(n),使得当n趋近于无穷大时,T(n)/f(n)的极限值为不等于零的常数,则称f(n)是T(n)的同数量级函数。
记作T(n)=O(f(n)),称O(f(n))为算法的渐进时间复杂度,简称时间复杂度。
T(n)不同,但时间复杂度可能相同。如:T(n) = n2+5n+6与T(n) = 3n2+3n+2它们的T(n)不同,但是时间复杂度相同,都为O(n2)。
(3)计算时间复杂度的方法:
用常数1代替运行实践中的所有加法常数。修改后的运行次数函数中,只保留最高阶项,取出最高阶项的系数。
注意:时间复杂度并不是用来计算算法运行的确切时间的,它是用来表示代码执行时间的增长变化趋势的。
2.平均时间复杂度和最坏时间复杂度
平均时间复杂度是指所有可能的输入实例均以等概率出现的情况下,算法的运行时间。
最坏情况下的时间复杂度为最坏时间复杂度。一般讨论的时间复杂度均是最坏情况下的时间复杂度。这样做的原因是:最坏情况下的时间复杂度是算法在任何输入实例上运行时间的界限。
这就保证了算法的运行时间不会比最坏情况更长。
3.常见的时间复杂度
常数阶O(1)
对数阶O(log2n)
线性阶O(n)
线性对数阶O(nlog2n)
平方阶O(n2)
立方阶O(n3)
k次方阶O(nk)
指数阶O(2n)
随着问题规模的不断增大,上述时间复杂度不断增大,算法的执行效率越低。
4.时间复杂度的例子
(1)常数阶 O(1)
1 int i = 1; 2 int j = 2; 3 ++i; 4 j++; 5 int m = i + j;
代码中没有循环等复杂结构,算法复杂度都为O(1)。
(2)线性阶 O(n)
for(int i = 0;i < n;i++) { j = i; j++; }
循环中会执行n次,所以消耗的时间随着n变化,时间复杂度为O(n)。
(3)对数阶 O(logN)
int i = 1; while(i < n) { i = i * 2; }
在while循环中,i每次都会乘以2,之后就会越来越接近n,在循环x次之后退出循环,2^x = n,所以x = log2n,其时间复杂度记为O(logN)。
(4)线性对数阶 O(nlogN)
将前面两种结合起来就是线性对数阶。因为O(nlogN) = nO(logN)。
(5)对数阶 O(n2)
循环嵌套就属于一种。
5.空间复杂度
空间复杂度是对一个算法在运行过程中临时占用存储空间大小的一个量度,同样反映的是一个趋势,用 S(n) 来定义。
空间复杂度比较常用的有:O(1)、O(n)、O(n²)。
6.空间复杂度计算
(1)O(1)
1 int i = 1; 2 int j = 2; 3 ++i; 4 j++; 5 int m = i + j;
代码中的i,j,m所分配的空间都不随着处理数据量变化,因此它的空间复杂度 S(n) = O(1)。
(2)O(n)
int[] m = new int[n] for(i=1; i<=n; ++i){ j = i; j++; }
在第一行中new了一个数组,占用内存大小为n,2-6行中虽然有循环,但是并没有分配内存,所以总的占用内存为O(n)。