复杂度分析

1.为何需要复杂度分析

我们大可以将代码跑一便,通过统计、监控等手段可以得到算法执行时间和占用内存情况,为什么还要做时间空间复杂度分析呢?这种方法其实也是正确的,叫做事后统计法,但是这种方法具有非常大的局限性,其原因如下:

  • 测试结果非常依赖测试环境
  • 测试结果受数据规模影响较大

所以,我们通常使用时间复杂度空间复杂度两种方式表示算法的复杂度

2.大O时间复杂度表示

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

把CPU执行每行代码的时间看做相同,称为unit_time,如上代码时间复杂度:T(n) = 2n + 4,用大O时间复杂度表示:T(n) = O(n)

一段代码执行时间与行数有关,所以抽象出一个公式:

  • T(n) = O(f(n))
  • 其中O表示代码的执行时间T(n)与f(n)表达式成正比
  • 也就是所有代码的执行时间T(n)与每行代码执行次数n成正比
  • 大O时间复杂度代表的不是代码真正的执行时间,而是代码执行时间随数据规模增长的一种渐进趋势,所以也叫做渐进时间复杂度,简称时间复杂度
  • 大O时间复杂度只需要记录最大量级
int cal(int n){
    int sum = 0;
    int i = 1;
    int j = 1;
    for(i = 1; i < n; ++ i){
        j = 1;
        for(j = 1; j <= n; ++ j){
            sum = sum + i;
        }
    }
    return sum;
}

上述代码时间复杂度:T(n) = O(2n^2 + 2n + 3),用大O时间复杂度表示:T(n) = O(n ^ 2)

3.常用的分析时间复杂度的方法

3.1.只关心循环次数最多的代码

适用于代码中只要一个循环体

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

T(n) = O(n)

3.2.加法法则,总复杂度等于量级最大的代码

适用于代码中有多个循环体

int cal(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;
}

T(n) = O(n ^ 2)

3.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;
}

T(n) = O(n ^ 2)

4.常见的复杂度量级

复杂度量级分为两类:

  • 多项式时间复杂度
    • 常数级时间复杂度 O(1)
      只要代码数据规模不随n的增长变化
    • 对数级时间复杂度 O(log n)
    • 线性阶 O(n)
    • O(m + n)
      由于m和n的数据规模不定,因此使用加法原则保留两者的复杂度
    • 线性对数阶 O(nlog n)
    • 平方阶 O(n ^ 2)
    • 立方阶 O(n ^ 3)
    • k次方阶 O(n ^ k)
  • 非多项式时间复杂度
    • 增数阶 O(2 ^ n)
    • 阶乘阶 O(n!)

当n越来越大时,非多项式的时间复杂度急剧增加,因此非多项式的算法基本不用

5.最好、最坏、平均时间复杂度

int find(int[] array, int n, int x){
    int i = 0;
    int pos = -1;
    for(; i < n; ++ i){
        if(array[i] == x){
            pos = i;
            break;
        }
    }
    return pos;
}

最好情况:O(1)
最坏情况:O(n)
平均(加权计算,基本和最坏保持一致):O(n)


总结:O(1) < O(log n) < O(n) < O(nlog n) < O(n ^ 2)

6.空间复杂度

一个算法在运行过程中临时占用存储空间大小的度量,一般有如下公式:

  • S(n) = O(f(n))
  • 随代码规模增加占用的空间也正向增加
  • 单次函数调用空间复杂度就是O(1),递归调用空间复杂度就是O(n)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值