1.概念
(1) 算法
非形式的说,算法(algorithm)就是任何良定义的计算过程,该过程取某个值或值的集合作为输入并产生某个值或值的集合作为输出。这样算法就是把输入转换为输出的计算步骤的一个序列。
可以认为,算法是求解计算问题的工具。
(2) 时间复杂度
时间复杂度是指执行算法所需要的计算工作量,即计算机执行相关程序所需要的时间(通过时间来评估效率)。
算法的时间复杂度是一个函数,它定性描述该算法的运行时间。是一个代表算法输入值的字符串的长度的函数。
时间复杂度常用大O符号表述,不包括这个函数的低阶项和首项系数。
使用这种方式时,时间复杂度可被称为是渐近的,亦即考察输入值大小趋近无穷时的情况。
简单理解就是:
- 用 “大O” 表示 “时间复杂度”,示例: O(n)
- 用一个函数表达算法复杂度的值,格式:O(具体不同的函数)
- 它定性的描述“运行时间”
- 它是渐进的,趋向接近的
O:数学上一个式子的上界(不能精确时间),即大约、估计的意思。
(3) 其他相关概念
定义
算法的输入大小:算法所需的输入值或值的集合为 n;
算法的执行次数函数:,也称语句频度或时间频度,算法的需要的运算次数即可用 输入大小的函数 来表示,即 T(n);
算法的执行时间的增长速度:我们记作 f(n)。
以c语言为例(累加程序,输入n为累加次数,输出sum为累加和)
int main(int n){
int sum,int;
sum=0;
for(i=0;i<n;i++){
sum=sum+i;
}
return sum;
}
输入为1和输入为100的情况,for循环的执行次数明显是不一样的。
即输入大小n,影响了算法的执行次数,不同的算法有不同的影响机制,因为算法执行次数记为以n为自变量的因变量,T(n)。
再谈执行时间的增长速度
随着n的增长,执行时间不断增大,时间的增长速度也有变化,但也与输入n有关,可以记为f(n)。
也可以看出,我们可以用 f(n) 的增长速度来度量 T(n) 的增长速度。
(速度与加速度)
自此,引入时间复杂度O(f(n))
2.计算
(1)O(f(n))基础
定义:
存在常数 c 和函数 f(N),使得当 N >= c 时 ,T(N) <= f(N)。
表示为 T(n) = O(f(n)) 。
如图:
当 N >= 2 的时候,f(n) = n^2 总是大于 T(n) = n + 2 的,于是我们说 f(n) 的增长速度是大于或者等于 T(n) 的,也说 f(n) 是 T(n) 的上界,可以表示为 T(n) = O(f(n))。
因为f(n) 的增长速度是大于或者等于 T(n) 的,即T(n) = O(f(n)),所以我们可以用 f(n) 的增长速度来度量 T(n) 的增长速度,所以我们说这个算法的时间复杂度是 O(f(n))。
此部分描述转自简书
作者:raymondCaptain
链接:https://www.jianshu.com/p/f4cca5ce055a
(2) 由T(n)到O(f(n))[!!!]
-
常数项
这对函数的增长速度影响并不大,所以当 T(n) = 常数c 时,时间复杂度为 O(1); 如果 T(n) 不等于一个常数项时,直接将常数项省略。
比如,T(n) = 2,时间复杂度为 O(1)。
T(n) = n + 29,时间复杂度为 O(n)。 -
高次项
这对函数的增长速度影响最大。n3的增长速度远大于n2。同时因为要求的精度不高,所以我们直接忽略低此项。
比如, T(n) = n^3 + n^2 + 29,此时时间复杂度为 O(n^3)。 -
阶数
因为函数的阶数对函数的增长速度的影响是最显著的,所以我们忽略与最高阶相乘的常数。
比如, T(n) = 3n3,此时时间复杂度为 O(n^3)。
(3) 策略
从内向外分析,从最深层开始分析。如果遇到函数调用,要深入函数进行分析。
- 对于一个循环,假设循环体的时间复杂度为 O(n),循环次数为 m,则这个循环的时间复杂度为 O(n×m)。
- 对于多个循环,假设循环体的时间复杂度为 O(n),各个循环的循环次数分别是a, b,c…,则这个循环的时间复杂度为O(n×a×b×c…)。分析的时候应该由里向外分析这些循环。
- 对于顺序执行的语句或者算法,总的时间复杂度等于其中最大的时间复杂度。
- 对于条件判断语句,总的时间复杂度等于其中 时间复杂度最大的路径 的时间复杂度。
3.示例
一般来说,时间复杂度高的算法比复杂度低的算法慢。
常见的时间复杂度(按效率排序):
O(1)<O(logn)<O(n)<O(nlogn)<O(n2)<O(n2logn)<O(n3)
常数阶 O(1)
function test(){
printf("hi!");
printf("hi!");
printf("hi!");
}
执行了三次printf,运行次数很固定,是个常数。那么时间复杂度就是O(3),取为O(1)。
线性阶 O(n)
for(i=0;i<n;i++){
sum=sum+i;
}
执行n 次,时间复杂度就是O(n)
平方阶 O(n2)
for(i=0;i<n;i++){
for(j=0;j<n;j++){
sum=sum+j+i;
}
}
循环,里面循环执行了n次,外层循环也执行了n次,所以时间复杂度为O(n^2)
立方阶 O(n3)
与上面类似,就是 三个 for 循环
对数阶:O(log2n)
while(n>1){
n=n/2;
}
即不断除以2,
n, n/2, n/2/2, n/2/2/2,n/2/2/,…
n/(2m)=1;我们需要算出m, 转换成n=2m,得出m=log2n,所以时间复杂度为O(logn)