算法的复杂度
算法复杂度分为:时间复杂度
和空间复杂度
。
- 时间复杂度:度量算法执行的时间长短。
- 空间复杂度:度量算法所需存储空间的大小。
度量一个算法执行时间的方法有两种
:
- 事后统计法:执行完之后统计时间。局限性非常大,例如,如果算法执行的时间很长,就会浪费时间,同时算法执行的时间的长短也会受电脑硬件的影响。
- 事前估计的方法:通过分析某个算法的
时间复杂度
来判断哪个算法更优。
在给出间复杂度的概念之前先来看看什么是时间频度
?
-
时间频度:一个算法执行时花费的时间与算法中语句的执行次数成正比,算法执行的语句越多这个算法所花费的时间就越长。
-
一个算法中
语句执行的次数
称为:语句频度或时间频度。 -
举个栗子
计算1到100的和
第一种:使用for循环
int sum=0;
int n=100
for(int i=1;i<=n;i++){
sum+=i;
}
时间频度T(n)=n+1
注意:因为最后还要在判断一次,所以为n+1
第二种:使用算法
int sum-=0;
int end=100;
sum=((100+1)*100)/2;
时间频度T(n)=1
- 关于时间频度的三个结论:
1)随着n的变大可以忽略
常数项
2)随着n的变大可以忽略
低次项
3)随着n的变大可以忽略
系数
1)随着n的变大可以忽略常数项
例如:n+20和n+30
随着n的增大可以忽略常数项20和30
2)随着n的变大可以忽略低次项
例如:n^2+3n+10和n^2+4n+50
随着n的变大可以忽略3n+10和4n+50
3)随着n的变大可以忽略系数
4n^2+3n+10和2n^2+4n+50
随着n的增大首先忽略低次项剩下:4n^2和2n^2
然后忽略4和2,最后变为n^2和n^2
时间复杂度:
- 一般情况下,算法中的基本操作语句的重复执行次数是问题规模n的某个函数,使用
T(n)
表示,如果有某个辅助函数f(n)
,当n趋向于无穷大时,T(n)/f(n)
的极限为不等于零的常数,则称f(n)
是T(n)
的同数量级函数,记做T(n)=O(f(n))
,我们称O(f(n))
为:算法的渐进时间复杂度,简称时间复杂度
,这种记法也叫大O记法。 - 注意:我们一般用
f(n)
来表示执行一个算法所需要的步骤,对于相同的T(n)
可能会有不同的f(n)
如何推导大O记法:
- 1,使用常数1取代运行时间中的所有
加法
- 2,在修改后的运行次数函数中,
只保留最高阶项
- 3,如果最高阶项存在且不是1,则去除与这个项相乘的常数,得到的结果就是大O阶
- 举个栗子:
/*执行一个次数为n的循环*/
public void function01(int n) {
for (int j = 0; j < n; j++) {
/*时间复杂度为O(1)的程序步骤序列*/
}
}
public void function02(){
int n=100;
function01(n);/*执行次数n次*/
for(int i=0;i<n;i++){/*执行次数n^2次*/
function01(n);
}
for(int i=0;i<n;i++){
for (int j=i;j<n;j++){/*执行次数 n(n+1)/2 次*/
/*时间复杂度为O(1)的程序步骤序列*/
}
}
}
第一步:f(n)=1+n+n²+n(n+1)/2=3/2*n²+3/2*n+1
第二步:O(f(n))=3/2*n²
第三步:O(f(n))=n²
时间复杂度为:n²
常见的时间复杂度大小:
常数阶<对数阶<线性阶<线性对数阶<平方阶<立方阶<K次方阶<指数阶
O(1)<O(log2n)<O(n)<O(nlog2n)<O(n^2)<O(n^3)<O(n^K)<O(2^n)
不同时间复杂度案例
- 常数阶
int i=1;
int j=2;
++i;
++j;
int value=i+j;
- 对数阶
int i=1;
while(i<n){
i=i*2;
}
上面的代码中的:i=i*2,展开来说就是1*2*2*2*2*2....
假设有x个2相乘后得到的数为n
也就是:2^x=n,x=log2^n
- 线性阶:单层for循环
for(int i=1;i<n;i++){
/*时间复杂度为O(1)的程序步骤序列*/
}
-
线性对数阶:单层for循环中嵌套对数阶
-
平方阶:两层个for循环
-
立方阶:三层for循环
-
K次方阶:K层for循环
平均时间复杂度和最坏时间复杂度
- 最坏情况下的时间复杂度称为最坏时间复杂度,一般讨论的时间复杂度都是最坏情况下的时间复杂度,这样就保证算法执行时间不会比这更长。
空间复杂度:
-
从上面推导时间复杂度的公式来看,时间复杂度不是用来计算程序
具体
耗时的,那么我也应该明白,空间复杂度也不是用来计算程序实际
占用的空间的。 -
空间复杂度是对一个算法在运行过程中
临时占用存储空间大小的一个量度
,同样反映的是一个趋势,我们用 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[] arr = new int[n]
for(i=1; i<=n; ++i)
{
j = i;
j++;
}
分析:这段代码中,第一行new了一个数组出来,这个数据占用的大小为n,这段代码的2-6行,虽然有循环,但没有再分配新的空间,因此,这段代码的空间复杂度主要看第一行即可,即 S(n) = O(n)