1、介绍
一个算法的优劣程度主要取决于程序执行时间和计算占用存储空间来衡量。
- 时间复杂度 :计算的是 算法执行语句的次数,可判断算法需要消耗时间长短,消耗时间越少越好。
- 空间复杂度:是算法在运行过程中临时 消耗存储空间的大小,消耗存储空间越少越好。
2、时间复杂度
2.1、表示方法:
一般使用“大O符号表示法”来表示时间复杂度:T(n) = O(f(n)),其中n是影响复杂度变化的因子,f(n)是复杂度具体的算法。
2.2、常见的时间复杂度:
描述 | 大O表示法 |
---|---|
常数阶 | O(1) |
线性阶 | O(n) |
对数阶 | O(logN) |
线性对数阶 | O(nlogN) |
平方阶 | O( n 2 n ^{2} n2) |
立方阶 | O( n 3 n^{3} n3) |
K次方阶 | O( n k n^{k} nk) |
指数阶 | O ( 2 n (2^{n} (2n) |
O ( 1 ) < O ( l o g n ) < O ( n ) < O ( n l o g n ) < O ( n 2 ) < O ( n 3 ) < O ( 2 n ) < O ( n ! ) < O ( n n ) O(1)<O(logn)<O(n)<O(nlogn)<O(n^{2})<O(n^{3})<O(2^{n})<O(n!)<O(n^{n}) O(1)<O(logn)<O(n)<O(nlogn)<O(n2)<O(n3)<O(2n)<O(n!)<O(nn)
注: 随着n的不断增大,时间复杂度不断增大,算法花费的时间越多。
2.3、计算方法:
- 选取相对增长最高的项
- 最高项的系数都化为1
- 若是常数的话都用O(1)表示
如: f ( n ) = 2 ∗ n 3 + 2 n + 100 = n 3 f(n) = 2*n^{3}+2n+100=n^{3} f(n)=2∗n3+2n+100=n3
2.4、最坏情况与平均情况
当查找一个有n个随机数字数组arr[n]中的某个数字时,最好的情况是第一次查找时就是,那么算法的时间复杂度为O(1),但是也有可能为最后一次查找才查到,那么时间复杂度为O(n)。
平均运行时间是期望的运行时间。
最坏运行时间是一种保证。在应用中,这是一种最重要需求,通常除非特别指定,我们提到的运行时间都是最坏的运行时间。
2.5、部分实例
- 对数阶O(log n n n)
i = 1;
while(i<= n)
{
i = i*2;
}
运行次数:
1
+
1
+
l
o
g
2
n
1+1+log_{2}n
1+1+log2n
则T(n) =
1
+
1
+
l
o
g
2
n
1+1+log_{2}n
1+1+log2n
则时间复杂度为:
l
o
g
2
n
log_{2}n
log2n 或(
l
o
g
2
n
log_2n
log2n)
注: 这里的底数对于研究程序运行效率不重要,常数部分常忽略!
- 平方阶O( n 2 n^{2} n2)
sum =0;
total = 0;
for(i=1;i<=n;i++)
{
sum = sum+j;
for(j=1;j<=n;j++)
total = total + i*j;
}
运行次数:
1
+
1
+
n
+
n
+
n
2
+
n
2
1+1+n+n+n^{2}+n^{2}
1+1+n+n+n2+n2
则T(n) =
2
n
2
+
2
n
+
2
2n^{2}+2n+2
2n2+2n+2
则时间复杂度为:O(
n
2
n^{2}
n2)
3、空间复杂度
3.1、空间复杂度O(1)
如果算法执行所需要的临时空间不随着某个变量n的大小而变化,则空间复杂度为一个常量,可表示为O(1)。
int m = 5;
int n = 8;
m = m + n;
n = m + n;
代码中m,n所分配的空间都不随着处理数据量变化,因此空间复杂度为S(n) = O(1)。
3.2、空间复杂度O(n)
int [] a = new int[n+1];//定义一个n+1的数组,0空间未使用
a[1] = 1;
a[2] = 2;
for(int i=3;i<=n;i++)
a[i] = a[i-1] + a[i-2];
代码中,第一行new出一个空间n+1的数组,后面变量没有分配空间,故占用空间为n+1,即为S(n) = O(n)。
注: 对于递归算法,计算一般为递推和回归,每一次递推需要一个栈空间来保存调用记录,因此空间复杂度一般空间复杂度为O(n)。
4、总结
评价一个算法效率主要是看它的时间复杂度和空间复杂度情况。在开发中,尤其在并发量的情况下,时间复杂度和空间复杂度的优化能带来巨大性能的提升。在算法优化中也常存在“时间”换“空间”,"空间"换“时间”的情况。