时间复杂度
度量程序执行时间的两个方法
- 事后统计,根据算法的实际运行时间来分析
问题
* 过度依赖计算机的硬件设施,无法体现出算法的优越性
* 需要程序完全运行才能比较,花费过多时间 - 事前估算,根据算法的时间复杂度来计算
时间频度T(n)
1.基本介绍:* 一个算法中语句执行的次数称之为时间频度 *
一个算法花费的时间与算法中语句执行的次数成正比例,即那个算法执行的语句次数越多,那就花费时间越多
- 例子:100的累加,一种从一加到一百,随着你输入的n的增大,语句的执行次数逐渐增大
sum = 0;
for(int i = 0;i <= 100; i ++){
sum = sum + i;
}
另一种,用公式来说,无论你要计算的数字由多大,最后都时运行一次
sum = (1 + i ) * i / 2;
2.计算说明
- 忽略常数项:
- 忽略低次项:
- 高次项的系数可以忽略:
时间复杂度
-
基本价绍:如果存在函数f(n),使得当n趋于无穷大时,T(n)/f(n)为非零的常数,,那么f(n)就是和T(n)同数量级的函数,记作T(n) = O(f(n)),O(f(n))称渐进时间复杂度,简称时间复杂度。
-
计算方法:用常数项代替所有时间频度T(n)中的常数项——》只保留最高次项——》最高次项的系数
T(n) = 2n2 + 7n + 9
——》 T(n) =2 n2 + 7n + 1
——》 T(n) = 2n2
——》 T(n) = n2,
其中n2即为对应的时间复杂度。
时间频度不同,他的时间复杂度可能相同 -
常见的时间复杂度:
- 常数阶,O(1):没有循环,递归等复杂结构,运行的时间不随着变量的增长而增长,无论其中的变量怎么变,都只是运行固定的次数
int i = 1;
i ++;
- 对数阶,O(log2 n): 2x > n,运行次数为2的X次方,指的就是这个,底数取决于是几次方
int i = 1;
while(i < n){
i = i * 2;
}
- 线性阶O(n):常见的是for()循环中的迭代,运行几次取决于你要输入n,
int j = 1;
for(int i = 0;i < n ;i ++){
j ++;
}
- 线性对数阶O(n log N):for循环代表的是线性阶,运行次数直接取决于你输入的n,while循环对应的是对数阶,嵌套在for循环中,就执行了n次,n * O(log N),执行了n次log循环
for(int m = 1;m < n;m ++){
int i = 1;
while(i < n){
i = i * 2;
}
}
- 平方阶O(n2):双层嵌套的for循环
int sum = 0;
for(int i = 0; i < n;i ++){
for(int j = 0;j < n;j ++){
sum ++;
}
}
- 立方阶O(n3):参照平方阶
- K次方阶O(nk):参照平方阶,还记得递归吗?
上述按照对应的从上往下的顺序,时间复杂度,依次增加。
平均时间复杂度和最坏时间复杂度
- 基本介绍:
- 平均时间复杂度:所有可能的输入实例等可能的出现时的运行时间
- 最坏时间复杂度:就是运行时间最长的情况
- 二者关系:取决于你的算法
- 以排序算法为例
排序法 | 平均时间复杂度 | 最坏时间复杂度 | 备注 |
---|---|---|---|
冒泡 | O(n2) | O(n2) | n小时比较好 |
交换 | O(n2) | O(n2) | n小时比较好 |
选择 | O(n2) | O(n2) | n小时比较好 |
插入 | O(n2) | O(n2) | 当大部分已经排序好再插入排序较好 |
基数 | O(logR B) | O(logR B | B是真数,R是基数 |
Shell | O(n log n) | O(ns) 1<s<2 | s是所选分组,n较大时好 |
快速 | O(n log n) | O(n2) | n较大时好 |
归并 | O(n log n) | O(n log n) | n较大时好 |
堆 | O(n log n) | O(n log n) | n较大时好 |
空间复杂度
基本介绍:程序运行时所占用的存储空间,定义以及算法同时间复杂度,都是n的函数
注:用户体验上更多是追求执行速度,运行时间,一些缓存产品和算法本质上有使用空间缓缓时间