0.概述
算法是程序世界的灵魂。对于同一个问题,使用不同的算法,也许最终得到的结果是一样的,但在过程中消耗的资源和时间却会有很大的区别;那么如何衡量不同算法之间的“优劣”或者性能了?一般来说从算法执行所消耗的「时间」和「空间」两个维度去考量:
- 时间维度:是指执行当前算法所消耗的时间,我们通常用「时间复杂度」来描述。
- 空间维度:是指执行当前算法需要占用多少内存空间,我们通常用「空间复杂度」来描述。
1.时间复杂度
一般情况下,使用符号O表示时间复杂度,即:T(n) = O(f(n));其中f(n) 表示每行代码执行次数之和,而 O 表示正比例关系,这个公式的全称是:算法的渐进时间复杂度。
常见的时间复杂度量级有如下,时间复杂度从上到下越来越大,执行的时间也越来越长。
1.1 O(1)
常数阶复杂度,代码执行时间是一个固定常数值,执行时间并不随着某个变量的增长而增长,那么无论这类代码有多长,即使有几万几十万行,都可以用O(1)来表示它的时间复杂度。
int i=1;
int b=1;
1.2 O(n)
线性阶复杂度,代码执行时间是根据入参线性增长的;
int sum=0;
for(int i=0;i<n;i++){
sum=sum+i;
}
1.3 O(logN)
对数阶复杂度,先观察如下代码:
public static int biSearch(int []array,int a){
int lo = 0;
int hi = array.length-1;
int mid;
while(lo <= hi) {
mid = lo + (hi - lo) / 2;
if(array[mid] == a) {
return mid;
}else if(array[mid] < a) {
lo = mid + 1;
}else{
hi = mid - 1;
}
}
return -1;
}
上面代码的执行时间差不多是这样的:
一次二分剩下:n/2
两次二分剩下:n/2/2 = n/4
…
m次二分剩下:n/(2^m)
在最坏情况下排除到只剩下最后一个值之后得到结果,即n/(2^m)=1,换算一下即:2^m=n,意即log2(n)。
1.4 O(nlogN)
线性对数阶,意即将时间复杂度为O(logn)的代码循环N遍的话,那么它的时间复杂度就是 n * O(logN),也就是了O(nlogN)。
for(m=1; m<n; m++)
{
i = 1;
while(i<n)
{
i = i * 2;
}
}
1.5 O(n²)
平方阶,相当于O(n) * O(n)=O(n²),线性阶在嵌套一个线性阶的循环。
for(x=1; i<=n; x++)
{
for(i=1; i<=n; i++)
{
j = i;
j++;
}
}
1.6 O(n³)/O(n^k)
立方阶或者K次方阶,类比平方阶,K次方阶即相当于嵌套循环了k次。
public int calcualte(int n,int k){
if(k<=0){
return n;
}
int m=0;
for(int i=0;i<n;i++){
m=m+i;
}
k--;
calcualte(m,k);
}
2. 空间复杂度
空间复杂度(Space Complexity)是对一个算法在运行过程中临时占用存储空间大小的量度。
2.1 O(1)
常量空间复杂度的算法,所使用的的空间不随被处理数据量n的大小而改变时。
int i = 1;
int j = 2;
++i;
j++;
int m = i + j;
2.2 O(n)
此算法的空间复杂度与n成线性比例关系,如下图所示,执行此算法所使用的的空间在第一行已经确认了,后续的计算都不会扩展空间。
int[] m = new int[n]
for(i=1; i<=n; ++i)
{
j = i;
j++;
}
还有其他复杂度的,欢迎大家来补充。
常用算法的时间和空间复杂度: