衡量算法的优劣,主要从时间和空间两个维度考虑:
时间维度:是指执行当前算法所消耗的时间,通常用「时间复杂度」来描述
空间维度:是指执行当前算法需要占用多少内存空间,通常用「空间复杂度」来描述
一、时间复杂度
把算法程序运行一遍,它所消耗的时间便可知,但是这样的方式弊端很大,容易受运行环境的影响,在性能高的机器上跑出来的结果与在性能低的机器上跑的结果相差会很大,且对测试时使用的数据规模也有很大关系。另外,在写算法的时候,亦不一定有办法完整的去在计算机上运行代码。
所以,一种通用的计算时间复杂度的方式,「 大O符号表示法 」诞生。即 T(n) = O(f(n))。
大O符号表示法中,时间复杂度的公式是: T(n) = O( f(n) )
其中f(n) 表示每行代码执行次数之和,而 O 表示正比例关系,这个公式的全称是:算法的渐进时间复杂度。
例子:
for(i=1; i<=n; ++i) // 执行 n+1 次
{
j = i; // 执行n次
j++; // 执行n次
}
假设上述代码是要执行的全部代码,当前程序执行了 3n +1 次,其时间复杂度为T(n) = O(n)
大O符号表示法并不是用于来真实代表算法的执行时间的,它是用来表示代码执行时间的增长变化趋势的。
如果n无限大的时候,T(n) = time(3n+1)中的常量1就没有意义了,倍数3也意义不大。因此直接简化为T(n) = O(n) 就可以了。
常见的时间复杂度量级有:
常数阶O(1) < 对数阶O(logN) < 线性阶O(n) < 线性对数阶O(nlogN) < 平方阶O(n²) < 立方阶O(n³) < K次方阶O(n^k) < 指数阶(2^n)
上述复杂度量级从左至右依次的时间复杂度越来越大,执行的效率越来越低。
1.常数阶O(1)
int i = 1;
int j = 2;
++i;
j++;
int m = i + j;
2.对数阶O(logN)
int i = 1;
while(i<n)
{
i = i * 2;
}
在while循环里面,每次都将 i 乘以 2,乘完之后,i 距离 n 越来越近。假设循环x次之后,i 大于 2 ,循环退出,即 2 的 x 次方等于 n,那么 x = log2^n 。也就是当循环 log2^n 次以后,代码结束。因此此段代码的时间复杂度为:O(logn)
3.线性阶O(n)
for(i=1; i<=n; ++i)
{
j = i;
j++;
}
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;
}
}
5.平方阶O(n²)
for(x=1; i<=n; x++)
{
for(i=1; i<=n; i++)
{
j = i;
j++;
}
}
若将其中一层循环的n改成m,即:, 它的时间复杂度就变成了 O(m*n)
for(x=1; i<=m; x++)
{
for(i=1; i<=n; i++)
{
j = i;
j++;
}
}
6.立方阶O(n³)、K次方阶O(n^k)
O(n³)相当于三层n循环,其它的类似。除此之外,其实还有 平均时间复杂度、均摊时间复杂度、最坏时间复杂度、最好时间复杂度 的分析方法。