时间复杂度基本理解

来源:互联网 发布:窗帘拼布算法 编辑:程序博客网 时间:2024/05/29 15:48

1、首先上一张图:




2、时间复杂度估算
再上图
         

           我们把 算法需要执行的运算次数用输入大小n的函数 表示,即 T(n) 。

就是说运算过程中该算法会执行T(n)次,然后我们定义一个函数f(n)来表示该执行函数的上限。

定义: 存在常数 c,使得当 N >= c 时 T(N) <= f(N),表示为 T(n) = O(f(n)) 

即在一定大的执行次数后,有一个上限即f(n)。

所以就用一个相对容易记录的函数的形式来粗略估计算法的时间复杂度了。

取值估算方法:

1)我们知道常数项并不影响函数的增长速度,所以当 T(n) = c,c 为一个常数的时候,我们说这个算法的时间复杂度为 O(1);
如果 T(n) 不等于一个常数项时,直接将常数项省略。
  2)我们知道高次项对于函数的增长速度的影响是最大的。n^3 的增长速度是远超 n^2 的,同时 n^2 的增长速度是远超 n 的。 同时因为要求的精度不高,所以我们直接忽略低此项。
  3)因为函数的阶数对函数的增长速度的影响是最显著的,所以我们忽略与最高阶相乘的常数。
3、大致推到过程
  1)
void aFunc(int n) {    for(int i = 0; i < n; i++) {         // 循环次数为 n        printf("Hello, World!\n");      // 循环体时间复杂度为 O(1)    }}
外层循环执行n次,内层执行一次所以一共就是n*1次,所以为O(n)

   
    2)

void aFunc(int n) {    for(int i = 0; i < n; i++) {         // 循环次数为 n        for(int j = 0; j < n; j++) {       // 循环次数为 n            printf("Hello, World!\n");      // 循环体时间复杂度为 O(1)        }    }}
其中每层循环执行n次就是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)对于条件判断语句,总的时间复杂度等于其中 时间复杂度最大的路径 的时间复杂度。

void aFunc(int n) {    if (n >= 0) {        // 第一条路径时间复杂度为 O(n^2)        for(int i = 0; i < n; i++) {            for(int j = 0; j < n; j++) {                printf("输入数据大于等于零\n");            }        }    } else {        // 第二条路径时间复杂度为 O(n)        for(int j = 0; j < n; j++) {            printf("输入数据小于零\n");        }    }}

此时时间复杂度为 max(O(n^2), O(n)),即 O(n^2)。


由此:基本策略为

从内向外分析,从最深层开始分析。如果遇到函数调用,要深入函数进行分析。
距离分析:
1、冒泡排序
void aFunc(int n) { for (int i = 0; i < n; i++) { for (int j = i; j < n; j++) { printf("Hello World\n"); } }}
从i=0内层开始,执行次数为:
T(n) = n + (n - 1) + (n - 2)……+ 1 = n(n + 1) / 2 = n^2 / 2 + n / 2。
取最高阶时间复杂度为:O(n^2)
2、
void aFunc(int n) {    for (int i = 2; i < n; i++) {        i *= 2;        printf("%i\n", i);    }}
首先假设执行了t次,

由于i<n,运行条件满足 2^t < n,所以执行次数t = log(2)(n),即 T(n) = log(2)(n),可见时间复杂度为 O(log(2)(n)),即 O(log n)。


3、

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)。
所以,总结一下,算法的时间复杂度其实就是运算次数的估计结果,遇到递归的时候考虑一下会不会跟O(2^n)有关,遇到内层有乘积指数的考虑会不会是对数。
基本的暂时这么多啦,进阶加油!




原创粉丝点击