数据结构与算法系列课程之二:复杂度分析(上)

数据结构和算法,本身就是要解决 “快” 和 “省” 的问题。考量的指标分别就是 “时间复杂度” 和 “空间复杂度”。

时间复杂度表示代码执行时间随着数据规模增长的变化趋势,也叫渐进时间复杂度。

空间复杂度,全称渐进空间复杂度,表示算法的存储空间数据规模之间的增长关系。

进行复杂度分析的原因:

事后统计法:简单来说,就是让代码在实际的平台跑一遍。

事后统计法有以下局限性:

1,测试结果非常依赖测试环境

2,测试结果受数据规模的影响很大

大O复杂度表示法:

T(n) = O(f(n))

T(n)表示代码的执行时间,n表示数据规模的大小,f(n)表示每行代码执行的次数总和

时间复杂度分析:

1,只关注循环执行次数最多的一段代码

代码示例:

int cal(int n){
    int sum = 0;
    int i = 1;
    for(; i <= n; ++i){
         sum = sum + i;   
    }
    return sum;
}

第2、3行代码都是常量级的执行时间,与n的大小无关,所以对于复杂度并没有影响。循环执行次数最多的是第4、5两行代码。

总的时间复杂度是O(n);

2,加法法则:总复杂度等于量级最大的那段代码的复杂度

代码示例:

int call(int n){
    int sum_1 = 0;
    int p = 1;
    for(; p < 100; ++p){
        sum_1 = sum_1 + p;
    }

    int sum_2 = 0;
    int q = 1;
    for(; q < 100; ++q){
        sum_2 = sum_2 + q;
    }

    int sum_3 = 0;
    int i = 1;
    int j = 1;
    for(; i <= n; ++i){
        j = 1;
        for(; j <= n; ++j){
            sum_3 = sum_3 + i*j;
        }
    }

    return sum_1 + sum_2 + sum_3;
}

第一段:sum_1部分,属于常量的执行时间。对于一个执行次数固定的代码,即使循环10w,100w次,只要是一个已知数,跟n无关,照样是常量级的执行时间。

第二段和第三段:时间复杂度分别是O(n)和O(n2).

总的时间复杂度等于量级最大的那段代码的时间复杂度。上例中:T(n) = O(n);

 

3,乘法法则:嵌套代码的复杂度等于嵌套内外代码复杂度的乘积

代码示例:

int cal(int n){
    int ret = 0;
    int i= 1;
    for (; i < n; ++i){
        ret = ret + f(i);
    }
}


int f(int n){
    int sum = 0;
    int i = 1;
    for(; i < n; ++i){
        sum = sum + i;
    }
    return sum;
}

以上示例中,cal()中嵌套调用了f(),复杂度为O(n)的函数嵌套调用复杂度为O(n)的函数,总的复杂度T(n) = T1(n) *T2(n) = O(n*n) = O(n2)。

常见时间复杂度量级(按数量级递增)

多项式量级:

  • 常量阶 O(1)
  • 对数阶 O(logn)
  • 线性阶 O(n)
  • 线性对数阶 O(nlogn)
  • 平方阶 O(n2)、立方阶 O(n3)、... k次方阶 O(nk):n的k次方

非多项式量级:

  • 指数阶 O(2n) 2的n次方
  • 阶乘阶 O(n!) 

以下几个常见的多项式时间复杂度:

1,常量阶 O(1)

执行次数是一个确定数字的代码,即执行时间不随n的增长而增大,和n没有关系,都记作 O(1)

2, O(logn)、 O(nlogn)

示例代码:

i = 1;
while( i <= n ){
    i = i*2;
}

以上代码,当2的k次方大于n时,循环终止,即执行次数:

k = \log_{2}n

对于O(n\log n),如果一段代码的时间复杂度是O(\log n),我们循环执行n次,则时间复杂度就是O(n\log n)

3,O(m+n) 、O(m*n)

实例代码:

in cal(int m, int n){
    int sum_1 = 0;
    int i = 1;
    for(; i < m; ++i){
        sum_1 = sum_1 +i;
    }

    int sum_2 = 0;
    int j = 1;
    for(; j < n; ++j){
        sum_2 = sum_2 +j;
    }

    return sum_1 + sum_2;
}

以上,无法事先评估 m 和 n 谁的量级大,所以无法利用加法准则,上面代码的时间复杂度就是O(m+n)

同理,当 m和n的代码出现嵌套,且无法判断m和n谁的量极大,时间复杂度对应的就是O(m*n)

空间复杂度分析

空间复杂度,全称渐进空间复杂度,表示算法的存储空间数据规模之间的增长关系。

代码实例:

void print(int n){
    int i = 0;
    int[] a = new int[n];
    for(i; i < n; ++i){
        a[i] = i * i;
    }

    for(i = n-1; i >= 0; --i){
        print out a[i]
    }
}

以上代码中,第二行i对应申请了一个空间存储变量,属于常量阶,和数据规模n没有关系,因此可以忽略。

第3行申请了一个大小为n的int 类型数组,除此之外,剩下的代码都没有占用更多的空间。

所以整段代码的空间复杂度就是O(n)

常见的空间复杂度是O(1) 、O(n)O(n^2),像O(\log n)O(n\log n) 这样的对数阶复杂度平时都用不到。

 

 

 

 

 

 

 

 

 

你可能感兴趣的:(数据结构,算法)