时间复杂度
算法效率的度量
- 时间复杂度
- 空间复杂度
算法时间复杂度
- 事前预估算法时间开销(n)与问题规模 n 的关系(T 表示 “time”)
代码demo
- c++编译是按照顺序编译的,所以函数需要放到main的上面
- demo1
#include <iostream>
/**
算法1 逐步递增 T(n) = 3n + 1003 = O(n);
结论1:顺序执行的代码只会影响常数项,可以忽略
结论2:只需挑循环中的一个基本操作分析它的执行次数与n的关系即可
*/
void loveYou(int n) {
int i = 1;
while (i < n)
{
i++;
printf("I Love You %d\n", i);
}
printf("I Love You More Than %d\n", n);
}
// 算法2 —— 嵌套循环计算
/**
时间开销与问题规模n的关系
T(n) = O(n) + O(n2) = O(n2);
结论三:如果有多层嵌套循环,只需关注最深层循环循环了几次
**/
void loveYou2(int n) { // n 为问题规模
int i = 1;
while (i < n) // 外层循环执行n次
{
i++;
printf("I Love You %d\n", i);
for (int j = 1; j <= n; j++) // 嵌套两层循环
{
printf("I am Iron Man\n"); // 内层循环共执行n2 次
}
}
printf("I Love You More Than %d\n", n);
}
// 算法3 —— 指数递增
/**
设最深层循环的语句频度(总共循环的次数)为x,则
由循环条件可知,循环结束时刚好满足2^x > n
x = log_2n + 1
T(n) = O(x) = O(log_2n)
*/
void loveYou3(int n) { // n 为 问题规模
int i = 1;
while (i < n)
{
i = i * 2; // 每次翻倍
printf("I Love You %d\n", i);
}
printf("I Lovw You More Than %d\n", n);
}
/**
算法4 —— 搜索数字型
计算上述算法的时间复杂度 T(n)
最好情况:元素 n 在第一个位置 —— 最好时间复杂度 T(n) = O(1);
最坏情况:元素 n 在最后一个位置 —— 最坏时间复杂度 T(n) = O(n);
平均情况:假设元素 n 在任意一个位置的概率相同为 1/n , —— 平均时间复杂度 T(n) = O(n);
循环次数 x = (1+2+3+……+n)1/n=(n(1+n)/2) * 1/n = (1+n/2)
*/
void loveYou4(int flag[], int n) { // n 为问题规模
printf("I Am Iron Man\n");
for (int i = 0; i < n; i++) // 从第一个元素开始查找
{
if (flag[i] == n) { // 找到元素n
printf("I Love You %d\n", n); // flag 数组中乱序存放了 1~n 这些数
break; // 找到后立即跳出循环
}
}
}
int main()
{
loveYou2(1000);
std::cout << "Hello World!\n";
}
- demo1语句频度:
- ① --1次
- ② --3001次
- ③ ④ --3000次
- ⑤ – 1次
T(3000) = 1 + 3001 + 2 * 3000 + 1
T(n) = 3n + 3
思考
问题一
是否可以忽略表达式某些部分?
- 当问题规模n足够大时……,
- 可以只考虑阶数高的部分
- 举例
- T(n) = 3n+3 约等于 T(n) = n
- T(n) = n2 + 3n +1000 约等于 T(n) = n2
- T(n) = n3 + n2 + 999999 约等于 T(n) = n3
大O表示“同阶”,同等数量级。即,当n->∞时,二者之比为常数
- 大O表示法
- T1(n) = O(n)
- T2 (n) = O(n2)
- T3 (n) = O(n3)
加法规则
-
多项相加,只保留最高阶的项,且系数变为1
-
T(n) = T1(n) + T2(n) = O(f(n)) + (Og(n)) = O(max(f(n), g(n)))