时间复杂度基本概念
算法的时间复杂度,用来度量算法的运行时间,记作: T(n) = O(f(n))。它表示随着 输入大小n 的增大,算法执行需要的时间的增长速度可以用 f(n) 来描述。
时间复杂度分析方法
1、只关注循环次数执行最多的一段的代码
int cal(int n){
int sum = 0;
for (int i = 1; i < n; i++) {
sum = sum + i;
}
return sum;
}
观察上述代码,因为赋值语句和返回语句都是常量级执行时间,与n是无关的,对于时间复杂度没什么影响;因此主要关心循环中的语句,发现循环执行n次,所以此段代码时间复杂度为:O(n)
2、嵌套代码的复杂度等于嵌套内外代码复杂度的乘积
int cal2(int n){
int sum = 0;
for (int i = 1; i < n; i++) {
for (int j = 1; j < n; j++) {
sum = sum + i * j;
}
}
return sum;
}
观察代码不难发现,题目中两个for循环,每个循环的复杂度为O(n),因为嵌套代码复杂度等于各自复杂度相乘,所以该段代码时间复杂度为O(n^2)
3、总复杂度等于最高阶项的复杂度
int cal3(int n){
int sum_1 = 0;
for (int p = 1; p < 100; p++) {
sum_1 = sum_1 + p;
}
int sum_2 = 0;
for (int q = 1; q < n; q++) {
sum_2 = sum_2 + q;
}
int sum_3 = 0;
for (int i = 1; i < n; i++) {
for (int j = 1; j < n; j++) {
sum_3 = sum_3 + i * j;
}
}
return sum_1 + sum_2 + sum_3;
}
sum_1下的循环,循环次数为100,因此是常量级执行时间,可以忽略不计;依次可以发现sum_2为O(n),sum_3为O(n^2);总时间复杂度:O(n^2 + n + 100) = O(n^2)
推导大O阶要点:
1.用常数1取代运行时间中的所有加法常数。
2.在修改后的运行次数函数中,只保留最高阶项。
3.如果最高阶项存在且不是1,则去除与这个项相乘的常数。得到的结果就是大O阶。
比如
T(n) = 999 ,此时时间复杂度为 O(1)
T(n) = n^2 + 100,此时时间复杂度为 O(n^2)。T(n) = 7n^3 + 4n^2 + 29,此时时间复杂度为 O(n^3)。
常见时间复杂度
数量级 | 名称 | 承受规模 | 常见算法 |
O(1) | 常数阶 | 任意 | 直接输出结果 |
O(n) | 线性阶 | 以百万计(五六百万) | 贪心算法、扫描和遍历 |
O(n²) | 平方阶 | 以千计数(两千) | 枚举、动态规划 |
O(logn) | 对数阶 | 任意 | 二分查找、快速幂 |
O(nlogn) | 线性对数阶 | 以十万计(三四十万) | 带有分治思想的算法,如二分法 |
O(n³) | 立方阶 | 不到两百 | 动态规划 |
O() | 指数阶 | 24 | 搜索 |
O(n!) | 阶乘阶 | 10 | 产生全排列 |
从小到大依次是:O(1)<o(logn)<O(n)< O(nlogn)< O()<O()<O()<O(n!)
注意:在工程算法实践中,算法时间复杂度达到O(),一般都没有什么意义了,要考虑优化算法; O()、O(n!)和O()时间复杂度问题称为多项式复杂程度的非确定性问题,简称NP.
补充
算法时间时间复杂度不止用一个数据规模表示,也有可能用两个或者多个数据规模来表示,列如下面代码:
int mnCal(int m, int n){
int sum_1 = 0;
for (int i = 0; i < m; i++) {
sum_1 = sum_1 + 1;
}
int sum_2 = 0;
for (int j = 0; j < n; j++) {
sum_2 = sum_2 + j;
}
return sum_1 + sum_2;
}
可以看出,sum_1下的复杂度为O(m),sum_2下的复杂度为O(n),因此总时间复杂度:O(m + n).