美文网首页
数据结构与算法之美(二)复杂度分析(上)

数据结构与算法之美(二)复杂度分析(上)

作者: sssummerr | 来源:发表于2019-11-01 18:06 被阅读0次

03 | 复杂度分析(上):如何分析、统计算法的执行效率和资源消耗?

执行效率是算法一个非常重要的考量指标。

为什么需要复杂度分析?

我们可以执行一遍代码,通过统计、监控得到算法的执行时间和占用内存大小(也叫事后统计法),但有以下局限性:

  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

第 2、3 行代码分别需要 1 个 unit_time 的时长,第 4 行代码都运行 n 遍,需要 n * unit_time 的时长,第 5 行同样运行 n 遍,需要 n * unit_time 的时长,所以这段代码的执行总时长是: (2 + 2n) * unit_time

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

再看这段代码:第 2、3、4 行分别需要 1 个 unit_time ,第 5、6 行分别需要 n * unit_time ,第 7、 8 行则分别需要 n^2 * unit_time ,所以这段代码的执行总时长是: (3 + 2n + 2n^2 ) * unit_time

所有代码的执行时长 T(n) 与每行代码执行次数n成正比,总结成一个公式:T(n) = O(f(n))

上述第一个例子可表示为 T(n) = O(2n + 2) ,第二个例子可表示为 T(n) = O(2n^2 + 2n + 3) 。这就是大O时间复杂度表示法,他表示的是代码执行时长随数据规模增长的变化趋势,也叫作渐进时间复杂度(asymptotic time complexity),简称时间复杂度

公式中的低阶、常量、系数三部分并不左右增长趋势,所以可以忽略,只需记录最大量级即可。所以用大O表示法表示刚刚两段代码的时间复杂度,记为:T(n) = O(n) T(n) = O(n^2)

时间复杂度分析

  1. 只关注循环执行次数最多的一段代码
  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 < n; ++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;
}
       
// 以上代码可先分别分析三部分的时间复杂度
// T1(n) = O(1)
// T2(n) = O(n)
// T3(n) = O(n^2)
// T(n) = T1(n) + T2(n) + T3(n) = max(O(1), O(n), O(n^2)) = O(n^2)
  1. 乘法法则:嵌套代码的复杂度等于嵌套内外代码复杂度的乘积
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() 函数执行,其时间复杂度 T1(n) = O(n),
// 但是循环体里执行了 f() ,f() 的时间复杂度 T2(n) = O(n),
// 所以以上代码的时间复杂度 T(n) = T1(n) * T2(n) = O(n^2)

几种常见时间复杂度实例分析

复杂度量级(按数量级递增)

  • 常量阶 O(1)
  • 对数阶 O(logn)
  • 线性阶 O(n)
  • 线性对数阶 O(nlogn)
  • 平方阶 O(n^2) 、立方阶 O(n^3) 、……k次方阶 O(n^k)
  • 指数阶 O(2^n)
  • 阶乘阶 O(n!)

对于以上复杂度量级可粗略分为两类,多项式量级非多项式量级。非多项式量级只有 O(2^n)O(n!)

时间复杂度为非多项式量级的算法问题叫作 NP(Non-Deterministic Polynomial)问题,当数据规模 n 越来越大时,非多项式量级算法的执行时长会急剧增加。所以,此类算法非常低效。

  1. O(1) :代码执行时长不随 n 的增大而变长,这样的代码时间复杂度记作O(1)
  2. O(logn)O(nlogn)
i = 1;
while (i <= n) {
  i = i * 2;
}
      
// i 从 1 开始取值,每循环一次,自乘 2 ,直到 i > n , 循环结束
// i 的取值是一个等比数列:1, 2, 4, 8, ...,2^x
// 当 2^x > n ,循环结束,即 x = log2n
// 这段代码的时间复杂度就是O(log2n)

实际上,不管是以几为底,对数阶的时间复杂度都记为 O(logn),因为对数之间是可以互相转化的,而在大O复杂度表示法中,是忽略系数的。
理解了 O(logn) ,那么当时间复杂度是 O(logn) 的代码执行 n 遍,根据乘法法则,这样的代码时间复杂度就是 O(nlogn) 了。

  1. O(m + n)O(m * n):由两个数据的规模决定时间复杂度
int 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 表示两个数据规模,无法评估谁的量级大
// 所以上述代码的时间复杂度 T(n) = O(m + n)

空间复杂度分析

时间复杂度的全称是渐进时间复杂度,表示算法的执行时间与数据规模之间的增长关系。类比一下,空间复杂度全称是渐进空间复杂度(asymptotic space complexity),表示算法的存储空间与数据规模之间的增长关系

void print(int n) {
  int i = 0;
  int[] a = new int[n];
  for (i; i < n; ++i) {
    a[i] = i * j;
  }
        
  for (i = n - 1; i >= 0; --i) {
    print out a[i];
  }
}
    
// 第 2 行代码申请了一个空间存储变量 i,但是它是常量阶的,与 n 无关,可以忽略
// 第 3 行代码申请了一个大小为 n 的 int 类型数组,所以整段代码的空间复杂度就是 O(n)

常见的空间复杂度:O(1) O(n) O(n^2)

相关文章

  • 数据结构与算法

    数据结构与算法之美 数据结构与算法之美1--如何学数据结构与算法之美2--复杂度分析(上)数据结构与算法之美3--...

  • 重温:数据结构与算法 - 02复杂度分析(二)

    数据结构与算法之美-学习大纲 上一节,学习了什么是大O复杂度分析、有哪些复杂度分析技巧、什么是时间复杂度、什么是空...

  • 《数据结构与算法之美--复杂度分析》

    《数据结构与算法之美--复杂度分析》 keyword: ​ 概念,大O表示法,常见的时间复杂度,最好、最坏、平...

  • 数据结构与算法之美(一):复杂度分析

    本章内容源于笔者对极客时间《数据结构与算法之美》以下章节的学习笔记: 复杂度分析(上):如何分析、统计算法的执行效...

  • 算法的复杂度分析

    本文是对极客时间《数据结构与算法之美》03-04 节课关于算法复杂度分析的小结。 前面:为什么要学习数据结构与算法...

  • 数据结构与算法-复杂度分析

    时间、空间复杂度:衡量算法执行小路的指标,数据结构与算法离不开时间、空间复杂度分析,复杂度分析是算法的精髓。 为什...

  • 数据结构与算法之时间复杂度分析

    复杂度分析,是所有数据结构与算法的重中之重,复杂度分析是整个算法学习的精髓,只要掌握了它,可以说数据结构和算法的内...

  • 2019-10-27文章阅读

    二分查找(上):如何用最省内存的方式实现快速查找功能? 来源:王争的数据结构与算法之美时间复杂度为O(logn) ...

  • 数据结构学习大纲

    第一章 绪论 数据结构基本概念数据结构基本概念算法的基本概念算法的时间复杂度与空间复杂度分析基础时间复杂度分析空间...

  • 复杂度分析(上)

    +文本内容是对王争《数据结构与算法之美》课程的笔记, 如果有任何侵权行为, 请联系博主删除 为什么需要复杂度分析?...

网友评论

      本文标题:数据结构与算法之美(二)复杂度分析(上)

      本文链接:https://www.haomeiwen.com/subject/rrrqbctx.html