网站别人给我做的备案 我能更改吗,河北网站建设备案价格,旅游自媒体网站怎么做,网站建设与管理vs2010十分钟搞定时间复杂度#xff08;算法的时间复杂度#xff09;
我们假设计算机运行一行基础代码需要执行一次运算。
int aFunc(void) {printf(Hello, World!\n); // 需要执行 1 次return 0; // 需要执行 1 次
}那么上面这个方法需要执行 2 次运算 …十分钟搞定时间复杂度算法的时间复杂度
我们假设计算机运行一行基础代码需要执行一次运算。
int aFunc(void) {printf(Hello, World!\n); // 需要执行 1 次return 0; // 需要执行 1 次
}那么上面这个方法需要执行 2 次运算
int aFunc(int n) {for(int i 0; in; i) { // 需要执行 (n 1) 次printf(Hello, World!\n); // 需要执行 n 次}return 0; // 需要执行 1 次
}这个方法需要 (n 1 n 1) 2n 2 次运算。
我们把 算法需要执行的运算次数 用 输入大小n 的函数 表示即 T(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))。
算法的时间复杂度用来度量算法的运行时间记作: T(n) O(f(n))。它表示随着 输入大小n 的增大算法执行需要的时间的增长速度可以用 f(n) 来描述。
显然如果 T(n) n^2那么 T(n) O(n^2)T(n) O(n^3)T(n) O(n^4) 都是成立的但是因为第一个 f(n) 的增长速度与 T(n) 是最接近的所以第一个是最好的选择所以我们说这个算法的复杂度是 O(n^2) 。
那么当我们拿到算法的执行次数函数 T(n) 之后怎么得到算法的时间复杂度呢
1、我们知道常数项对函数的增长速度影响并不大所以当 T(n) cc 为一个常数的时候我们说这个算法的时间复杂度为 O(1)如果 T(n) 不等于一个常数项时直接将常数项省略。
比如
第一个 Hello, World 的例子中 T(n) 2所以我们说那个函数(算法)的时间复杂度为 O(1)。
T(n) n 29此时时间复杂度为 O(n)。2、我们知道高次项对于函数的增长速度的影响是最大的。n^3 的增长速度是远超 n^2 的同时 n^2 的增长速度是远超 n 的。 同时因为要求的精度不高所以我们直接忽略低此项。
比如
T(n) n^3 n^2 29此时时间复杂度为 O(n^3)。3、因为函数的阶数对函数的增长速度的影响是最显著的所以我们忽略与最高阶相乘的常数。
比如
T(n) 3n^3此时时间复杂度为 O(n^3)。综合起来如果一个算法的执行次数是 T(n)那么只保留最高次项同时忽略最高项的系数后得到函数 f(n)此时算法的时间复杂度就是 O(f(n))。为了方便描述下文称此为 大O推导法。
由此可见由执行次数 T(n) 得到时间复杂度并不困难很多时候困难的是从算法通过分析和数学运算得到 T(n)。对此提供下列四个便利的法则这些法则都是可以简单推导出来的总结出来以便提高效率。
1、对于一个循环假设循环体的时间复杂度为 O(n)循环次数为 m则这个循环的时间复杂度为 O(n×m)。
void aFunc(int n) {for(int i 0; i n; i) { // 循环次数为 nprintf(Hello, World!\n); // 循环体时间复杂度为 O(1)}
}此时时间复杂度为 O(n × 1)即 O(n)。
2、对于多个循环假设循环体的时间复杂度为 O(n)各个循环的循环次数分别是a, b, c…则这个循环的时间复杂度为 O(n×a×b×c…)。分析的时候应该由里向外分析这些循环。
void aFunc(int n) {for(int i 0; i n; i) { // 循环次数为 nfor(int j 0; j n; j) { // 循环次数为 nprintf(Hello, World!\n); // 循环体时间复杂度为 O(1)}}
}此时时间复杂度为 O(n × n × 1)即 O(n^2)。
3、对于顺序执行的语句或者算法总的时间复杂度等于其中最大的时间复杂度。
void aFunc(int n) {// 第一部分时间复杂度为 O(n^2)for(int i 0; i n; i) {for(int j 0; j n; j) {printf(Hello, World!\n);}}// 第二部分时间复杂度为 O(n)for(int j 0; j n; j) {printf(Hello, World!\n);}
}此时时间复杂度为 max(O(n^2), O(n))即 O(n^2)。
4、对于条件判断语句总的时间复杂度等于其中 时间复杂度最大的路径 的时间复杂度。
此时时间复杂度为 max(O(n^2), O(n))即 O(n^2)。
时间复杂度分析的基本策略是从内向外分析从最深层开始分析。如果遇到函数调用要深入函数进行分析。
最后我们来练习一下
一. 基础题 求该方法的时间复杂度
void aFunc(int n) {for (int i 0; i n; i) {for (int j i; j n; j) {printf(Hello World\n);}}
}参考答案 当 i 0 时内循环执行 n 次运算当 i 1 时内循环执行 n - 1 次运算……当 i n - 1 时内循环执行 1 次运算。 所以执行次数 T(n) n (n - 1) (n - 2)…… 1 n(n 1) / 2 n^2 / 2 n / 2。 根据上文说的 大O推导法 可以知道此时时间复杂度为 O(n^2)。
二. 进阶题 求该方法的时间复杂度
void aFunc(int n) {for (int i 2; i n; i) {i * 2;printf(%i\n, i);}
}参考答案 假设循环次数为 t则循环条件满足 2^t n。 可以得出执行次数t log(2)(n)即 T(n) log(2)(n)可见时间复杂度为 O(log(2)(n))即 O(log n)。
三. 再次进阶 求该方法的时间复杂度
long aFunc(int n) {if (n 1) {return 1;} else {return aFunc(n - 1) aFunc(n - 2);}
}参考答案 显然运行次数T(0) T(1) 1同时 T(n) T(n - 1) T(n - 2) 1这里的 1 是其中的加法算一次执行。 显然 T(n) T(n - 1) T(n - 2) 是一个斐波那契数列通过归纳证明法可以证明当 n 1 时 T(n) (5/3)^n同时当 n 4 时 T(n) (3/2)^n。 所以该方法的时间复杂度可以表示为 O((5/3)^n)简化后为 O(2^n)。 可见这个方法所需的运行时间是以指数的速度增长的。如果大家感兴趣可以试下分别用 110100 的输入大小来测试下算法的运行时间相信大家会感受到时间复杂度的无穷魅力。