复杂度分析
衡量编写的算法代码的执行效率:时间、空间复杂度分析
为什么需要复杂度分析
把代码运行一遍不就可以了嘛,这种叫事后统计法,但是有非常大的局限性。
1.测试结果非常依赖测试环境
测试环境中的硬件的不同会对测试结果有很大的影响
2.测试结果受数据规模的影响很大
如果数据是有序的,那排序算法不需要做任何操作,执行时间很短,如果测试数据规模太小,测试结果可能无法真实地反映算法的性能,比如对于小规模的数据排序,插入排序可能反倒会比快排要快
大O复杂度表示法
算法的执行效率就是算法代码执行的时间
比如一段1,2,3,……,n的累加和①
int cal(int n ){
int sum = 0 ;
int i = 1;
for(;i<=n;++i){
sum = sum + i ;
}
return sum;
}
假设每行代码执行的时间都一样,为unit_time,第2,3行代码需要1个unit_time执行时间,第4,5行需要n遍,总共是(2n+2)*unit_time,看出来,所有代码的执行时间T(n)与每行代码 的执行次数成正比 ②
int cal(int n ){
int sum = 0 ;
int i = 1;
int j = 1;
for(;i<=n;++i){
j = 1;
for(;j<=n;++j){
sum = sum + i * j;
}
}
}
第5,6行代码循环执行了n遍,需要2nunit_time,第7,8行代码循环执行了n²遍,需要2n² * unit_time遍的执行时间,所以总运行时间为(2n²+2n+3) unit_time
所有代码的执行时间T(n)与每行代码的执行次数n成正比:
T(n)= O(f(n)) T(n)代表执行时间,n表示数据规模的大小,f(n)表示每行代码执行的次数总和,公式中的O,表示执行时间T(n)与f(n)成正比
①中T(n) = O(2n+2) ②中T(n)=O(2n²+2n+3),大O时间复杂度表示代码执行时间随着数据规模增长的变化趋势,当n很大时候,公式中的低阶、常量、系数三部分都可以忽略,只需要记录最大量级即可,①可表示O(n) ②为O(n²)
时间复杂度分析
1.只关注循环执行次数最多的一段代码
①中第4,5行代码循环执行次数最多,所以O(n)
2.加法法则:总复杂度等于量级最大的那段代码的复杂度
3.乘法法则:嵌套代码的复杂度等于嵌套内外代码复杂度的乘积
所有代码的复杂度量级
常 量 阶 O ( 1 ) 指 数 阶 O ( 2 n ) 对 数 阶 O ( l o g n ) 阶 乘 阶 ( n ! ) 线 性 阶 O ( n ) 线 性 对 数 阶 O ( n l o g n ) 平 方 阶 O ( n 2 ) 常量阶 O(1) 指数阶O(2^n)对数阶O(logn)阶乘阶(n!)线性阶O(n)线性对数阶O(nlogn)平方阶O(n^2) 常量阶O(1)指数阶O(2n)对数阶O(logn)阶乘阶(n!)线性阶O(n)线性对数阶O(nlogn)平方阶O(n2)
立 方 阶 O ( n 3 ) … … k 次 方 阶 O ( n k ) 立方阶O(n^3)……k次方阶O(n^k) 立方阶O(n3)……k次方阶O(nk)
分为多项式量级和非多项式量级(只有O( 2 n 2^n 2n)和O(n!)),我们把时间复杂度为非多项式量级的算法问题叫做NP问题
当数据规模n越来越大的时候,非多项式量级算法的执行时间会急剧增加,非多项式时间复杂度的算法是非常低效的算法,主要看常见的多项式时间复杂度
ps:为什么O( 2 n 2^n 2n)和O(n!)是非多项式时间复杂度算法?是因为它的规模n没有出现在底数的位置 ,当n越来越大的时候,它的增加就是急剧增加
1.O(1)
只要代码的执行时间不随n的增大而增大,只要算法中不存在循环语句、递归语句都算。
2.O(logn)、O(nlogn)
i = 1 ;
while( i <= n){
i = i * 2;
}
从代码中,i的取值为 2 0 、 2 1 、 2 2 、 … … 、 2 x = n 2^0、2^1、2^2、……、2^x=n 20、21、22、……、2x=n,只要当 2 x = n 2^x=n 2x=n的时候,就知道代码执行的次数,可知 x = log 2 n x=\log_2n x=log2n,所以这段时间复杂度就是O( log 2 n \log_2n log2n)
对数之间可以互相转换,在采用大O标记复杂度的时候忽略系数,所以不管以2为底,还是以3为底,在对数阶时间复杂度的表示方法里,统一表示为O( l o g n logn logn)
如果那一段代码循环执行n遍,时间复杂度就是O(nlogn),归并排序、快速排序时间复杂度都是O(nlogn)
3.O(m+n)、O(m*n)
int cal(int n,int m){
int sum_1 = 0;
int i = 1;
for(;i < m;++i){
sum_1 = sum_1 +i;
}
int sum_2 = 0;
int j = 1;
for(; j < n ;++j){
sum_2 = sum_2 + j;
}
return sum_1 + sum_2;
}
m和n都是两个数据规模,但是无法事先评估两者量级谁大,所以不能简单的利用加法法则,所以时间复杂度就是O(m+n)
空间复杂度分析
算法的存储空间与数据规模之间的增长关系
void print(int n){
int i = 0 ;
int[] a = new int[n];
for(i;i < n;++i){
a[i] = i * i;
}
for(i = n -1;i >= 0 ; --i){
print out a[i]
}
}
第二行代码中,申请了一个空间存储变量i,但是是常量阶的,与数据规模n无关,第三行申请了一个大小为n的int类型数组,所以代码的空间复杂度就是O(n)
常用的空间复杂度就是O(1),O(n)、O( n 2 n^2 n2)