二、算法复杂度分析

如何分析、统计算法的执行效率和资源消耗?

时间、空间复杂度分析。

为什么需要复杂度分析?

你可能会有些疑惑,我把代码跑一遍,通过统计、监控,就能得到算法执行的时间和占用的内存大 小。为什么还要做时间、空间复杂度分析呢?这种分析方法能比我实实在在跑一遍得到的数据更准 确吗? 首先,我可以肯定地说,你这种评估算法执行效率的方法是正确的。很多数据结构和算法书籍还给 这种方法起了一个名字,叫事后统计法。但是,这种统计方法有非常大的局限性。

1.测试结果非常依赖测试环境。

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

3.大O复杂度表示法

1 int cal(int n) {
2   int sum = 0;
3   int i = 1;
4   for (; i <= n; ++i) {
5     sum = sum + i;
6   }
7   return sum;
8 }

从 CPU 的角度来看,这段代码的每一行都执行着类似的操作:读数据 - 运算 - 写数据。尽管每行代码 对应的 CPU 执行的个数、执行的时间都不一样,但是,我们这里只是粗略估计,所以可以假设每 行代码执行的时间都一样,为 unit_time 。在这个假设的基础之上,这段代码的总执行时间是多少 呢? 第 2 、 3 行代码分别需要 1 个 unit_time 的执行时间,第 4 、 5 行都运行了 n 遍,所以需要 2nunit_time 的执行时间,所以这段代码总的执行时间就是 (2n+2)*unit_time 。可以看出来,所有代 码的执行时间 T(n) 与每行代码的执行次数成正比。

按照这个分析思路,我们再来看这段代码。

1 int cal(int n) {
2   int sum = 0;
3   int i = 1;
4   int j = 1;
5   for (; i <= n; ++i) {
6     j = 1;
7     for (; j <= n; ++j) {
8       sum = sum +  i * j;
9     }
10   }
11 }

我们依旧假设每个语句的执行时间是 unit_time 。那这段代码的总执行时间 T(n) 是多少呢? 第 2 、 3 、 4 行代码,每行都需要 1 个 unit_time 的执行时间,第 5 、 6 行代码循环执行了 n 遍,需要

2n * unit_time的执行时间,第7,8行执行了n^2遍,所以需要2n^2*unit_time的执行时间。所以整段代码执行时间

T(n)=(2n^2+2n+3)*unit_time.

尽管我们不知道 unit_time 的具体值,但是通过这两段代码执行时间的推导过程,我们可以得到一 个非常重要的规律,那就是,所有代码的执行时间 T(n) 与每行代码的执行次数 n 成正比。 我们可以把这个规律总结成一个公式。

大O记号

其中, T(n)表示代码执行的时间; n 表示数据规模的大小; f(n) 表示每行代码执行的次数总和。因为这是一个公式,所以用 f(n) 来表示。公式中的O ,表示代码的执行时间 T(n) 与 f(n) 表达式成正比。 所以,第一个例子中的 T(n) = O(2n+2) ,第二个例子中的 T(n) = O(2n +2n+3) 。

这就是大 O 时间复杂度表示法。

大 O 时间复杂度实际上并不具体表示代码真正的执行时间,而是表示代码执行时间随数据规模增长的变化趋势,所以,也叫作渐进时间复杂度( asymptotic time complexity ),简称时间复杂度。 当 n 很大时,你可以把它想象成 10000 、 100000 。而公式中的低阶、常量、系数三部分并不左右增 长趋势,所以都可以忽略。我们只需要记录一个最大量级就可以了,如果用大 O 表示法表示刚讲的 那两段代码的时间复杂度,就可以记为: T(n) = O(n) ; T(n) = O(n ) 。

时间复杂度分析

如何分析一段代码的时间复杂度?有三个比较实用的方法。

  1. 只关注循环执行次数最多的一段代码 大 O 这种复杂度表示方法只是表示一种变化趋势。我们通常会忽略掉公式中的常量、低阶、系数,只需要记录一个最大阶的量级就可以了。所以,我们在分析一个算法、一段代码的时间复杂度的时候,也只关注循环执行次数最多的那一段代码就可以了。这段核心代码执行次数的 n的量级,就是整段要分析代码的时间复杂度。

    那前面的第一个例子来说,其中第 2 、 3 行代码都是常量级的执行时间,与 n 的大小无关,所以对于复杂度并没有影响。循环执行次数最多的是第 4 、 5 行代码,所以这块代码要重点分析。前面我们也讲过,这两行代码被执行了 n 次,所以总的时间复杂度就是 O(n)

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

//前100个数相加
int cal(int n) {int sum_1 = 0;int p = 1;for (; p < 100; ++p) {sum_1 = sum_1 + p;}//前n个数  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;}

这个代码分为三部分,分别是求 sum_1 、 sum_2 、 sum_3 。我们可以分别分析每一部分的时间复杂 度,然后把它们放到一块儿,再取一个量级最大的作为整段代码的复杂度。

第一段的时间复杂度是多少呢?这段代码循环执行了 100 次,所以是一个常量的执行时间,跟 n 的 规模无关。 这里我要再强调一下,即便这段代码循环 10000 次、 100000 次,只要是一个已知的数,跟 n 无 关,照样也是常量级的执行时间。当 n 无限大的时候,就可以忽略。尽管对代码的执行时间会有很 大影响,但是回到时间复杂度的概念来说,它表示的是一个算法执行效率与数据规模增长的变化趋 势,所以不管常量的执行时间多大,我们都可以忽略掉。因为它本身对增长趋势并没有影响。 那第二段代码和第三段代码的时间复杂度是多少呢?答案是 O(n) 和 O(n )。

综合三段代码的时间复杂度,我们取最大的量级,即总的复杂度为O(n^2)

也就是说:总的时间复杂度就等于量级最大的那段代码的时间复杂度。那我们将这个规律抽 象成公式就是: 如果 T1(n)=O(f(n)) , T2(n)=O(g(n)) ;那么 T(n)=T1(n)+T2(n)=max(O(f(n)), O(g(n))) =O(max(f(n),g(n))).

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

如果T1(n)=O(f(n)),T2(n)=O(g(n));

那么T(n)=T1(n)xT2(n)=O(f(n))xO(g(n))=O(f(n)xg(n)). 也就是说,假设T1(n)=O(n),T2(n)=O(n^2),则T1(n)xT2(n)=O(n^3)。

落实到具体的代码上

1 int cal(int n) {
2   int ret = 0;
3   int i = 1;
4   for (; i < n; ++i) {
5     ret = ret + f(i);
6   }
7 }
8
9 int f(int n) {
10  int sum = 0;
11  int i = 1;
12  for (; i < n; ++i) {
13    sum = sum + i;
14  }
15  return sum;
16 }

我们单独看cal()函数。假设f()只是一个普通的操作,那第4~6行的时间复杂度就是,T1(n)=O(n)。

但f()函数本身不是一个简单的操作,它的时间复杂度是T2(n)=O(n),所以,整个cal)函数的时间复杂度就是,T(n)=T1(n)xT2(n)=O(nxn)=O(n2)。

常见的时间复杂度实例分析

分类:

多项式量级:

非多项式量级:O(2n)和O(n!)。

我们把复杂度为非多项式量级的算法问题叫做NP(Non-Deterministic Polynomial,非确定多项式)问题。

当数据规模n越来越大时,非多项式量级算法的执行时间会急剧增加,求解问题的执行时间会无限增长。所以,非多项式时间复杂度的算法其实是非常低效的算法。因此,关于NP时间复杂度问题略。

主要来看几种常见的多项式时间复杂度。

Next…

转载于:https://www.cnblogs.com/fenqinearl/p/10849415.html

数据结构与算法之美02相关推荐

  1. 数据结构与算法之美 02 | 如何抓住重点

    什么是数据结构? 数据结构是指一组数据的存储结构. 什么是算法? 算法就是操作数据结构的一组方法. 数据结构是为算法服务的,算法要作用在特定的数据结构之上. 想要学习数据结构与算法,首先要掌握一个数据 ...

  2. mysql索引用trie树_数据结构与算法之美【完整版】

    资源目录: ├─01-开篇词 (1讲) │ ├─00丨开篇词丨从今天起,跨过"数据结构与算法"这道坎.html │ ├─00丨开篇词丨从今天起,跨过"数据结构与算法&qu ...

  3. 《数据结构与算法之美》目录

    数据结构与算法之美_算法实战_算法面试 开篇词 (1讲) <数据结构与算法之美>学习指导手册 开篇词 | 从今天起,跨过"数据结构与算法"这道坎 入门篇 (4讲) 01 ...

  4. 王争数据结构与算法之美开篇问题整理

    数据结构与算法之美笔记整理 为什么大多数编程语言中数组从 0 而不是从 1 开始编号? 从数组存储的内存模型上来看,"下标"最确切的定义应该是"偏移(offset)&qu ...

  5. 数据结构与算法之美(一):概论

    最近在极客时间上面学习王争老师的课程<数据结构与算法之美>,以前虽然学过一些皮毛,但是不够精,作为程序员的基本内功,还是要继续学习.至此通过总结的方式,把这门课的要点记录下来,供自己思考回 ...

  6. 推荐学习-数据结构与算法之美

    推荐一个学习资源:数据结构与算法之美.主要包括以下几个学习内容: 20个经典数据结构与算法 100个真实项目场景案例 文科生都能看懂的算法手绘图解 轻松搞定BAT的面试通关秘籍 作者:王争 前谷歌工程 ...

  7. 极客时间 自我提升第二天 数据结构与算法之美 应该掌握 / 趣谈网络原理 / 深入浅出计算机组成原理 思维导图

    菜鸟今天又来完成所说的诺言,也希望大家督促,在今天的学习中,菜鸟有了新的认知,我会将上一篇中理解不完善的一些地方进行补充,学习本就是不断打破自己的认知,如果思考都不做,何来的知识的积累 文章目录 数据 ...

  8. 干货教程:数据结构与算法之美

    特别放送 第⼀期:数据结构与算法学习书单 第⼆期:争哥独家学习⼼得 第三期:算法实战测试题 第四期:⼤咖的专栏学习⽅法 ⽤户故事 1:这⼀年我的脑海⾥只有算法 ⽤户故事 2:只有站在思维的⾼处,才有⾜ ...

  9. 数据结构与算法之美笔记——基础篇(中):树,二叉树,二叉查找树,平衡二叉查找树,红黑树,递归树,堆

    树: A 节点就是 B 节点的父节点,B 节点是 A 节点的子节点.B.C.D 这三个节点的父节点是同一个节点,所以它们之间互称为兄弟节点.我们把没有父节点的节点叫作根节点,也就是图中的节点 E.我们 ...

最新文章

  1. 高效Transformer层出不穷,谷歌团队综述文章一网打尽
  2. 气泡图在开源监控工具中的应用效果
  3. SCRIPT LOAD lua文件
  4. XMLHttpRequest 对象
  5. PHP5中的魔术方法
  6. 简单java在线测评程序
  7. Android后台服务---无交互时的Service
  8. 手机APP移动应用开发
  9. 揭露QPS增高后的秘密
  10. 动态的显示当前的时间---setInterval的用法
  11. 中国最惨创业者的惨痛教训!
  12. JMeter接口压力测试实战教程
  13. 隐藏隧道通信:lcx 端口转发
  14. 当耐克用上了AI、AR技术,你的鞋也要放飞自我了?Just Do It !
  15. SUN ZFS STORAGE 7320阵列管理
  16. windows主机和ubuntu互传文件的4种方法
  17. XTU,C语言,平衡三进制2
  18. EOS区块链的通信模型
  19. 读JQuery 有感
  20. 浅谈压缩感知(十三):压缩感知与传统压缩

热门文章

  1. java 自动封装_自动补全的java封装
  2. c语言构造满二叉树,递归创建二叉树c语言实现+详细解释
  3. mysql表创建在哪_mysql创建表命令是哪句
  4. http_build_query的用法
  5. 今年Java面试必问的这些技术面,看完这一篇你就懂了
  6. java核心技术面试精讲
  7. python【蓝桥杯vip练习题库】ALGO-148 5-1最小公倍数(GCD)
  8. 华为鸿蒙系统支持智慧多屏吗,搭载鸿蒙OS!华为宣布企业智慧屏:多屏协同、底座带轮子...
  9. python桌面图标被删了_Python实现图标锁定到Windows任务栏或删除图标
  10. python简单爬虫入门一_Python简单爬虫入门二