文章目录

  • 前言
  • 一、复杂度分析的意义
  • 二、复杂度分析基础
    • 1. 大 O O O复杂度表示法
    • 2. 时间复杂度分析方法
      • 2.1 加法法则
      • 2.2 乘法法则
    • 3. 空间复杂度
  • 总结
    • 1. O ( 1 ) O(1) O(1)
    • 2. O ( l o g n ) 、 O ( n l o g n ) O(logn)、O(nlogn) O(logn)、O(nlogn)
    • 3. O ( m + n ) 、 O ( m n ) O(m+n)、O(mn) O(m+n)、O(mn)

前言

复杂度分析是算法学习的基础,也是算法的精髓。针对每一个数据结构和算法,通过复杂度分析,我们能够更加科学地判断其质量好坏。因此,复杂度分析非常重要。


一、复杂度分析的意义

虽然,我们通过把代码运行一遍,能够很快得出运行时间,内存占用的数据。然而,在实际的生产测试环境中,由于硬件的不同,这种方法不够准确。

另外,当我们使用不同的测试数据,也会对测试结果产生较大影响。比如排序,众所周知,在数据量小的时候插入排序会比快排快很多。在极端情况下,比如数据已经排好序时,有些排序算法甚至不会做任何操作!

而复杂度分析其实就是一种帮助你在不运行代码的情况下,估算代码执行时间和效率的方法。


二、复杂度分析基础

1. 大 O O O复杂度表示法

代码(示例):

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

对芯片来说,代码的每一条语句都执行了类似于:读数据—>运算—>写数据这样的操作。

我们假设每条语句在同一台电脑上的运行时间一样,设为 u n i t _ t i m e unit\_time unit_time。

我们很轻易就能看出来执行第2、3、7行分别需要1个 u n i t _ t i m e unit\_time unit_time,而第4、5行代码循环运行了n遍,需要
( 2 n + 3 ) × (2n+3)\times (2n+3)× u n i t _ t i m e unit\_time unit_time的执行时间。

综上可得,这段很silly的示例代码总的执行时间是
( 2 n + 3 ) × (2n+3)\times (2n+3)× u n i t _ t i m e unit\_time unit_time。通过这个例子,得出一个简单结论,任何一段代码的总的执行时间,记为 T ( n ) T(n) T(n)(该例子中的 ( 2 n + 3 ) × (2n+3)\times (2n+3)× u n i t _ t i m e unit\_time unit_time)与每一条语句的执行次数的累加数(该例子里的 2 n + 3 2n+3 2n+3)成正比。

我们可以把这一规律总结为如下公式:
T ( n ) = O ( f ( n ) ) T(n) = O(f(n)) T(n)=O(f(n))其中 T ( n ) T(n) T(n)表示代码执行的总时间, n n n表示数据规模, f ( n ) f(n) f(n)表示每条语句执行次数的累加和。

套用这个例子中的
T ( n ) = ( 2 n + 3 ) × u n i t _ t i m e = O ( 2 n + 3 ) T(n) = (2n+3)\times unit\_time = O(2n+3) T(n)=(2n+3)×unit_time=O(2n+3)但是,我们并不使用大 O O O表示代码的真正执行时间,而用它来表示代码执行时间随着数据规模增大的变化趋势。

当 n n n很大的时候, f ( n ) f(n) f(n)多项式中的低阶项、常量、系数并不决定增长的趋势,我们忽略这些部分,只保留最大量级,也就是最高阶。

所以,上面例子中的 O ( 2 n + 3 ) O(2n+3) O(2n+3) 应该记为 O ( n ) O(n) O(n)。


2. 时间复杂度分析方法

时间复杂度全称为渐进时间复杂度,表示代码执行时间随着数据规模增大的变化趋势。

2.1 加法法则

代码总的复杂度等于量级最大的那段代码的复杂度

代码(示例):

int cal(int n) {int sumOne = 0;int k = 1;for (; p <= 100; ++p) {sumOne = sumOne + p;}int sumTwo = 0;int i = 1;int j = 1;for (; i <= n; ++i) {j = 1;for (; j <= n; ++j) {sumTwo = sumTwo + i * j;}}return sumOne + sumTwo;
}

求这段代码复杂度的方法是分别求2-6行,8-19行的代码复杂度,然后求和。

2-6行需要的执行时间是 100 × u n i t _ t i m e 100\times unit\_time 100×unit_time,可以表示为 O ( 1 ) O(1) O(1)。

8-19行需要的执行时间是 ( 2 n 2 + 2 n + 4 ) × u n i t _ t i m e (2n^2+2n+4)\times unit\_time (2n2+2n+4)×unit_time,可以表示为 O ( n 2 ) O(n^2) O(n2)。

而总的时间复杂度,我们取量级最大的那部分代码的时间复杂度。这条法则就是加法公式:

i f if if
T 1 ( n ) = O ( f ( n ) ) ; T 2 ( n ) = O ( g ( n ) ) T1(n) = O(f(n)); T2(n) = O(g(n)) T1(n)=O(f(n));T2(n)=O(g(n))

t h e n then then
T ( n ) = T 1 ( n ) + T 2 ( n ) = m a x ( O ( f ( n ) ) , O ( g ( n ) ) ) T(n) = T1(n) +T2(n) = max(O(f(n)), O(g(n))) T(n)=T1(n)+T2(n)=max(O(f(n)),O(g(n)))
T ( n ) = O ( m a x ( f ( n ) , g ( n ) ) ) T(n) = O(max(f(n),g(n))) T(n)=O(max(f(n),g(n)))


2.2 乘法法则

嵌套代码的复杂度等于嵌套内外代码复杂度的乘积

代码(示例):

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

根据前面所述的方法我们很容易估算出上述代码中 cal( ) 函数z中4-6行的时间复杂度是 O ( n ) O(n) O(n),fun( ) 函数的时间复杂度也是 O ( n ) O(n) O(n)。

而我们的乘法法则规定:
i f if if
T 1 ( n ) = O ( f ( n ) ) ; T 2 ( n ) = O ( g ( n ) ) T1(n) = O(f(n));T2(n)=O(g(n)) T1(n)=O(f(n));T2(n)=O(g(n))
t h e n then then
T ( n ) = T 1 ( n ) × T 2 ( n ) = O ( f ( n ) ) × O ( g ( n ) ) T(n) = T1(n)\times T2(n) = O(f(n))\times O(g(n)) T(n)=T1(n)×T2(n)=O(f(n))×O(g(n))
T ( n ) = O ( f ( n ) × g ( n ) ) T(n) = O(f(n)\times g(n)) T(n)=O(f(n)×g(n))

所以,在这个例子中套用乘法公式cal()函数的时间复杂度 T ( n ) = T 1 ( n ) × T 2 ( n ) = O ( n × n ) = O ( n 2 ) T(n) = T1(n)\times T2(n) = O(n\times n) = O(n^2) T(n)=T1(n)×T2(n)=O(n×n)=O(n2)


3. 空间复杂度

空间复杂度仍然使用 大 O O O 复杂度表示法。空间复杂度的全称是渐进空间复杂度,表示算法的存储空间与数据规模的增长关系。

public void reverse(int a[], int n) {int tmp[] = new int[n];for (int i = 0; i < n; ++i) {tmp[i] = a[n-i-1];}for (int i = 0; i < n; ++i) {a[i] = tmp[i];}
}

在第3行,申请了一段空间来存储变量 i ,但是这是一个常量阶的,与数据规模n无关,在表示空间复杂度时可以将之省略。而第二行代码中申请的给数组的存储空间的大小是 n ,剩下的代码没有占用更多的空间。所以整段代码的空间复杂度是 O ( n ) O(n) O(n)。

可以与时间复杂度进行类比,常见的类型与之相同,比时间复杂度简单。


总结

虽然代码千差万别,但是常见的时间复杂度并不多。总结一下只有以下这几种:

1. O ( 1 ) O(1) O(1)

只要代码的执行时间不随规模 n n n变化,就是常量级时间复杂度,全部记作 O ( 1 ) O(1) O(1)。

代码(示例):

int cal(int n) {int sumOne = 0;int k = 1;for (; p <= 100; ++p) {sumOne = sumOne + p;}
}

该段代码复杂度即为 O ( 1 ) O(1) O(1)。

2. O ( l o g n ) 、 O ( n l o g n ) O(logn)、O(nlogn) O(logn)、O(nlogn)

对数阶时间复杂度比较常见,但最难分析。下面用一个简单的具体例子作介绍:

代码(示例):

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

这段代码里的 i 实际上是一个等比数列,i 的取值序列是
2 0 , 2 1 , 2 2 , . . . , 2 x 2^0,2^1,2^2,...,2^x 20,21,22,...,2x。当 i ( 2 x 2^x 2x) > n 时,该循环就会终止。求出 x 的值就是 log ⁡ 2 n \log_2n log2​n ,因此这段代码的时间复杂度就是 O ( l o g 2 n ) O(log_2n) O(log2​n)。

但是对数的“底”即 log ⁡ 2 n \log_2n log2​n 中的2对代码执行时间增加趋势的影响很小,微乎其微,所以我们对于任意复杂度为 O ( l o g a n ) O(log_an) O(loga​n) 的算法,都把时间复杂度实际记作 O ( l o g n ) O(logn) O(logn)。

3. O ( m + n ) 、 O ( m n ) O(m+n)、O(mn) O(m+n)、O(mn)

有一种比较特别的情况,代码的时间复杂度由数据规模决定,下面看个例子。

代码(示例):

int cal(int m, int n) [int sumOne = 0;int i = 1;for (; i <= m; ++i) {sumOne = sumOne + i;}int sumTwo = 0;int j = 1;for (; j <= n; ++j) {sumTwo = sumTwo + j;}return sumOne + sumTwo;
}

该例子中的代码里,m 和 n 表示两个无关的数据规模。关于这两个数据规模,因为在事先无法估出谁的量级更大,所以在表示时间复杂度的时候,我们不能省略其中任何一个。综上,上面代码的时间复杂度为 O ( m + n ) O(m+n) O(m+n)。

算法复杂度分析(3600字)相关推荐

  1. 算法复杂度分析(下)

    前一篇文章算法复杂度分析(上)讲述了复杂度的大 O 表示法和几个分析原则,这篇文章我们来讲讲另外几种复杂度,最好情况时间复杂度(best case time complexity).最坏情况时间复杂度 ...

  2. 八大排序:Java实现八大排序及算法复杂度分析

    目录 QUESTION:八大排序:Java实现八大排序及算法复杂度分析 ANSWER: 一:冒泡排序 1.算法分析 2.时间复杂度分析 3.代码 二:选择排序 1.算法分析 2.时间复杂度分析 3.代 ...

  3. 网络流问题以及EK算法复杂度分析

    网络流问题以及EK算法复杂度分析 一.网络流算法 通过一个例子引入网络流问题. 现有一个自来水厂要往家里通水,自来水厂用Vs表示,家用Vt表示.从自来水厂到家之间连接了很多水管,并且中途经过很多转接点 ...

  4. 算法复杂度分析看这一篇就够了

    执行效率是算法一个非常重要的考量指标,而时间复杂度和空间复杂度则是衡量算法代码的执行效率. 为什么需要复杂度分析 通常情况下,我们可以在写完代码的情况下把程序跑一遍,通过统计.监控,就能得出算法执行的 ...

  5. 算法之如何进行算法复杂度分析

    一.什么是复杂度分析? 1.数据结构和算法解决是"如何让计算机更快时间.更省空间的解决问题". 2.因此需从执行时间和占用空间两个维度来评估数据结构和算法的性能. 3.分别用时间复 ...

  6. 怎么算matlab算法复杂度,算法复杂度分析

    1. 何为数据结构?何为算法? 简单来说,数据结构就是数据的存储方式,比如数组就是把数据存在一段连续的内存上,而链表则是通过指针的关联将数据存在任意可用的内存上:栈是先进后出,队列是先进先出. 而算法 ...

  7. 算法复杂度分析(下):最好、最坏、平均、均摊等时间复杂度概述

    细化时间复杂度分析 代码千千万,有些代码逻辑会很复杂,所以为了更细化的分析算法的复杂度,再复杂度分析方面引入了4个知识点: 1.最好情况时间复杂度(best case time complexity) ...

  8. 【排序综合】直接插入排序,希尔排序,快速排序,堆排序,冒泡排序,简单选择排序的简介,实现和算法复杂度分析

    目录 1. 直接插入排序 1.1 直接插入排序简介 1. 什么是直接插入排序 2. 排序思想 1.2 排序实现 1. 排序代码 2. 复杂度分析: 3. 运行结果: 1.3 学习链接 2. 希尔排序( ...

  9. 欧几里德算法复杂度分析

    欧几里得算法 function Euclid(a; b) 1: if b = 0 then 2: return a; 3: end if 4: return Euclid(b; a mod b); 复 ...

最新文章

  1. 何恺明最新一作论文:无监督胜有监督,迁移学习无压力,刷新7项检测分割任务...
  2. eureka同步原理_eureka原理剖析
  3. 详解进程的虚拟内存,物理内存,共享内存
  4. 分布式文件系统研究-测试-上传文件测试
  5. 浅述几年建站SEO之路的失败与反思
  6. Getting started with caffe questions answers (摘选)
  7. C#2005中如何把unicode编码的数字转化为EBCDIC编码
  8. 苹果已开始研发蜂窝调制解调器 不只是用于iPhone
  9. vmware传东西到linux后文件变小,在VMWARE的Linux虚拟机上删除大容量文件后硬盘空间大小还不变怎么办?虚拟机虚拟机的各种应用及问题...
  10. Cache 和 Buffer 有什么区别
  11. 持续技术开放 | SOFAStack 启用独立 Group
  12. css div 分页样式,3种简洁漂亮的CSS分页按钮样式
  13. Method annotated with @Bean is called directly. Use dependency injection instead
  14. 怎样用计算机合并视频,怎么合并视频和字幕 格式工厂视频字幕合并教程-电脑教程...
  15. c语言mac地址加冒号,如何通过在Excel中添加冒号来格式化单元格中的mac地址?
  16. C# json解析字符串总是多出双引号_json从立地到成佛
  17. 淘淘商城23_Linux上的操作_solr集群的搭建、zookeeper集群的搭建
  18. sudo -i和sudo -s
  19. 解决Google Colab 读取Google Drive(云盘) 文件速度慢
  20. python的微积分运算

热门文章

  1. 相约一场非遗聚会,云南非遗影像展盛大开幕
  2. [LeetCode 1373]二叉搜索子树的最大键值和
  3. Netflix 总用户达到 2.325 亿;马斯克打脸创建 X.AI 公司;印度首开苹果门店;谷歌老板对 AI 很担心?特斯拉营收增加,但净利润下降…《经济学人 | 第 17 期 | 速读版》
  4. android RecyclerView 九宫格布局上下均分
  5. 雷布斯 22 年前写的代码 你见过吗?
  6. C/C++中的getenv()函数
  7. golang中ascll和string字符串的相互转化
  8. .NET通过脚本重启IIS服务
  9. 不为彼岸,只为大海-----海贼王
  10. 存取款(while循环实例)