算法时间复杂度(大O表示法)

最近在学习陈斌老师的数据结构与算法课程,打算将课程中的重要知识点总结在博客里,和大家一起分享。这是我第一次学习这方面的知识,如果有错误的地方,请一定指要出来呀,谢谢~

我们在对算法进行分析时,主要从计算资源消耗的角度来评判和比较算法。计算资源指标一般有两种,一种是算法解决问题过程中需要的存储空间或内存,由于存储空间受到问题自身数据规模的变化影响,要区分哪些存储空间是问题本身描述所需,哪些是算法占用,不容易。因此我们采用算法的执行时间来衡量计算资源。如果使用算法运行时间的实际检测来度量,会发现同一个算法,采用不同的编程语言编写,放在不同的机器上运行,得到的运行时间会不一样。我们需要更好的方法来衡量算法运行时间,这个指标与具体的机器、程序、运行时段都无关,即算法时间复杂度。

1 算法时间度量指标

  • 一个算法所实施的操作数量或步骤数可作为独立于具体程序/机器的度量指标。
  • 需要一种通用的基本操作来作为运行步骤的计量单位,这个通用的基本操作我们选择为赋值语句
  • 因此赋值语句执行次数可以作为算法时间度量的指标。
  • 问题规模是影响算法执行时间的主要因素,在下面例1这个算法中,需要累计的整数个数合适作为问题规模的指标(前100,000个整数求和对比前1,000个整数求和,算是同一问题的更大规模)。
  • 算法分析的目标是要找出问题规模会怎么影响一个算法的执行时间
    1 \color{red}{**例1**} :对于“问题规模”n,赋值语句数量为T(n)=1+n。
    在这里插入图片描述

2 数量级函数(大O表示法)

  • 基本操作数量函数T(n)的精确值并不是特别重要,重要的是T(n)中起决定性因素的主导部分(当问题规模增大的时候,T(n)中的一些部分会盖过其它部分的贡献)。
  • 数量级函数描述了T(n)中随着n增加而增加速度最快的主导部分,称作大O表示法,记作O(f(n)),其中f(n)表示T(n)中的主导部分。
  • 常见的大O数量级函数
    在这里插入图片描述 2 \color{red}{**例2**}
    1.对于T(n)=1+n,当n增大时,常数1显得越来越不重要,所以可以去掉1,保留n作为主要部分,运行时间数量级就是O(n)。
    2.对于 T ( n ) = 5 n 2 + 27 n + 1005 T(n)=5n^{2}+27n+1005 ,当n越来越大, n 2 n^{2} 项就越来越重要,其它两项对结果的影响则越来越小,运行时间数量级为 O ( n 2 ) O(n^{2})
    3.从代码分析确定执行时间数量级函数:以下代码赋值语句可以分为4个部分,根据下图分析, T ( n ) = 3 + 3 n 2 + 2 n + 1 = 3 n 2 + 2 n + 4 T(n) = 3+3n^{2}+2n+1 = 3n^{2}+2n+4 ,仅保留最高阶项 n 2 n^{2} ,去掉所有系数,数量级为 O ( n 2 ) O(n^{2})
    在这里插入图片描述

3 从“变位词”判断问题看算法时间复杂度

  • 问题描述:所谓“变位词”是指两个词之间存在组成字母的重新排列关系,如:heart和earth,python和typhon。
  • 解题目标:写一个bool函数,以两个词作为参数,返回这两个词是否变位词。
1 \color{blue}{*解法1:逐字检查*}

思路:

  • 将词1中的字符逐个到词2中检查是否存在
  • 存在就“打勾”标记(防止重复检查)
  • 如果每个字符都能找到,则两个词是变位词
  • 只要有1个字符找不到,就不是变位词
  • 如下图:
    在这里插入图片描述

程序代码:
在这里插入图片描述

算法分析:

  1. 问题规模:词中包含的字符个数n
  2. 主要部分在于两重循环(外层循环遍历s1每个字符,将内层循环执行n次;而内层循环在s2中查找字符,每个字符的对比次数,分别是1、2…n中的一个,而且各不相同)
  3. 所以总执行次数是1+2+3+……+n,数量级为O(n2)
2 \color{blue}{*解法2:排序比较*}

思路:

  • 将两个字符串都按照字母顺序排好序
  • 再逐个字符对比是否相同,如果相同则是变位词
  • 有任何不同就不是变位词
  • 如下图:
    在这里插入图片描述

程序代码:
在这里插入图片描述

算法分析:

  1. 问题规模:词中包含的字符个数n
  2. 粗看上去,本算法只有一个循环,最多执行n次,数量级是O(n)
  3. 但循环前面的两个sort并不是无代价的,排序算法采用不同的解决方案,其运行时间数量级差不多是O(n2)或者O(n log n),大过循环的O(n)
  4. 所以本算法时间主导的步骤是排序步骤,运行时间数量级就等于排序过程的数量级O(n log n)
3 \color{blue}{*解法3:计数比较*}

思路:

  • 对比两个词中每个字母出现的次数
  • 如果26个字母出现的次数都相同,这两个字符串就一定是变位词

程序代码:
在这里插入图片描述

算法分析:

  1. 问题规模:词中包含的字符个数n
  2. 计数比较算法中有3个循环迭代,但不像解法1那样存在嵌套循坏
  3. 前两个循环用于对字符串进行计数,操作次数等于字符串长度n;第3个循环用于计数器比较,操作次数总是26次
  4. 所以总操作次数T(n)=2n+26,其数量级为O(n)
  5. 但是本算法依赖于两个长度为26的计数器列表,来保存字符计数,这相比前2个算法需要更多的存储空间,如果考虑由大字符集构成的词(如中文具有上万不同字符),还会需要更多存储空间。牺牲存储空间来换取运行时间,或者相反,这种在时间空间之间的取舍和权衡,在选择问题解法的过程中经常会出现。

4 Python两种内置数据类型上各种操作的大O数量级

4.1 List列表数据类型

最常用的

  • 按索引取值和赋值(v=a[i], a[i]= v),由于列表的随机访问特性,这两个操作执行时间与列表大小无关,均为O(1)
  • 另一个是列表增长,可以选择append()和__add__()“+”,lst.append(v)执行时间是O(1),lst= lst+ [v]执行时间是O(n+k),其中k是被加的列表长度。

List基本操作的大O数量级

\color{red}{**注意!!**}

  • pop()从列表末尾移除元素,O(1),而pop(i)从列表中部移除元素,O(n)
  • 原因:从中部移除元素的话,要把移除元素后面的元素全部向前挪位复制一遍

在这里插入图片描述

4.2 dict数据类型

字典与列表不同,根据关键码(key)找到数据项,而列表是根据位置(index)

最常用的

  • 最常用的取值get和赋值set,其性能为O(1)
  • 另一个重要操作contains(in)是判断字典中是否存在某个关键码(key),这个性能也是O(1)

dict基本操作的大O数量级
在这里插入图片描述

4.3 更多Python数据类型操作复杂度

参考Python官方的算法复杂度网站:https://wiki.python.org/moin/TimeComplexity

参考资料:

https://www.icourse163.org/course/PKU-1206307812#/info

猜你喜欢

转载自blog.csdn.net/gaoxiaoxue_/article/details/108010257
今日推荐