数据结构与算法python版本之列表和字典复杂度

news2024/11/25 0:56:35

前面我们了解了大O表示法以及对不同算法的预估
接下来我们讨论python两种内置数据类型(列表和字典)上各种操作的大O数量级

列表数据类型

list类型各种操作的实现方法很多,如何选择具体哪种实现方法。总的方案就是,让最常用的操作性能最好,牺牲不太常用的操作。一般我们使用80/20原则——80%的功能其使用率只有20%;
列表最常用的操作是:按索引取值和赋值,比如说是v =a[i],a[i]=v,由于列表的随机访问特性,这两个操作执行时间与列表大小无关,均为O(1)
另一个最常用的是列表增长,我们可以选择append()和_add_()“+”;
list.append(v),执行时间是O(1)
lst=lst+[v],执行时间是O(n+k),其中k是被加的列表长度;
以上,我们选择哪个方法来操作列表,决定了程序的性能将会是什么样子的

所以我们接下来举个例子,以生成前n个整数列表的方法来进行测试

#循环连接列表方式生成
def test1():
    l = []
    for i in range(1000):
        l = l + [i]

#利用append方法添加元素生成
def test2():
    l = []
    for i in range(1000):
        l.append(i)

#用列表推导式来实现
def test3():
    l = [i for i in range(1000)]

#利用range函数调用转成列表
def test4():
    l = list(range(1000))

接下来使用timeit模块对函数计时
创建一个Timer对象,指定需要反复运行的语句和只需要运行一次的“安装语句"
然后调用这个对象的timeit方法,其中可以指定反复运行多少次

from timeit import Timer
t1 = Timer("test1()","from __main__ import test1")
print("concat %f seconds\n" % t1.timeit(number = 1000))
t2 = Timer("test2()","from __main__ import test2")
print("append %f seconds\n" % t2.timeit(number = 1000))
t3= Timer("test3()","from __main__ import test3")
print("comprehension %f seconds\n" % t3.timeit(number = 1000))
t4= Timer("test4()","from __main__ import test4")
print("list range %f second\n" % t4.timeit(number = 1000))

我们运行之后的结果是

D:\code\hwt\venv\Scripts\python.exe D:\code\hwt\test_b.py 
concat 0.906821 seconds

append 0.037925 seconds

comprehension 0.019203 seconds

list range 0.007087 second


Process finished with exit code 0

我们可以看到:
这四种方法运行时间差别很大:列表连接(concat)最慢,List range最快,速度相差近200倍。
append也要比concat快得多;另外,我们注意到列表推导式速度是append两倍的样子

这个是列表增加的操作,那么列表的删除操作的性能怎么样呢,我们来进行一下list.pop的计时试验
我们注意到pop这个操作,pop()它是从列表末尾移除元素,O(1);pop(i)从列表中部移除元素,O(n),这个的原因在于Python所选择的实现方法:从中部移除元素的话,要把移除元素后面的元素全部向前挪位复制一遍,这个看起来有点笨拙,但这种实现方法能够保证列表按索引取值和赋值的操作很快,达到O(1),这也算是一种对常用和不常用操作的折衷方案

为了验证表中的大O数量级,我们把两种情况下的pop操作来实际计时对比,相对同一个大小的list,分别调用pop()和pop(0)
对于大小不同的list做计时时,期望的结果是:pop()的时间不随list大小变化,pop(0)的时间随着list变大而边长

x = list(range(2000000))

import timeit
popzero = timeit.Timer("x.pop(0)", "from __main__ import x")
print(popzero.timeit(number=1000))
popend = timeit.Timer("x.pop()", "from __main__ import x")
print(popend.timeit(number=1000))

运行结果是

D:\code\hwt\venv\Scripts\python.exe D:\code\hwt\test_b.py 
1.2591895
3.379999999997274e-05

Process finished with exit code 0

首先我们看对比
对于长度两百万的列表,执行1000次
pop()时间是1.26
pop(0)时间是0.0000338
相差1000多倍

接下来我们通过改变列表的大小来测试两个操作的增长趋势
我们使用如下代码

x = list(range(2000000))

import timeit
popzero = timeit.Timer("x.pop(0)", "from __main__ import x")
print(popzero.timeit(number=1000))
popend = timeit.Timer("x.pop()", "from __main__ import x")
print(popend.timeit(number=1000))

print("pop(0)  pop()")
for i in range(1000000, 100000001, 1000000):
    x = list(range(i))
    pt = popend.timeit(number=1000)
    x = list(range(i))
    pz = popzero.timeit(number=1000)
    print("%15.5f, %15.5f" %(pz,pt))

运行之后我们发现

D:\code\hwt\venv\Scripts\python.exe D:\code\hwt\test_b.py 
1.3254912
3.1899999999973616e-05
pop(0)  pop()
        0.63952,         0.00010
        1.27070,         0.00005
        1.89366,         0.00005
        2.51875,         0.00007
        3.16086,         0.00008
        3.79605,         0.00006
        4.42389,         0.00010
        5.03627,         0.00005
        5.73751,         0.00007
        6.31563,         0.00005
        6.98311,         0.00005
        7.59439,         0.00004
        8.27622,         0.00005
        8.86239,         0.00006
        9.49110,         0.00005
       10.17818,         0.00005
       10.84629,         0.00008
       11.36126,         0.00005
       12.01135,         0.00005
       12.63843,         0.00005
       13.28525,         0.00006
       14.00205,         0.00005
       14.34231,         0.00005
       15.26307,         0.00006
       15.82224,         0.00005
       16.43155,         0.00006
       17.22533,         0.00005
       17.30603,         0.00005
       18.44659,         0.00005
       18.92079,         0.00005
       19.54118,         0.00005
       20.26459,         0.00005
       20.73825,         0.00007
       21.25046,         0.00005
       22.14043,         0.00006
       22.65790,         0.00005
       23.24152,         0.00011
       23.80041,         0.00005
       24.32684,         0.00005
       25.06700,         0.00012
       25.61303,         0.00005
       26.03183,         0.00005
       27.01421,         0.00005
       27.64547,         0.00006
       28.23284,         0.00005
       28.85573,         0.00007
       29.40643,         0.00009
       30.18097,         0.00005
       30.76568,         0.00008
       31.37746,         0.00005
       32.13112,         0.00005
       32.76765,         0.00005
       33.18907,         0.00006
       34.00520,         0.00006
       34.86553,         0.00005
       35.42342,         0.00007
       35.97052,         0.00005
       36.49784,         0.00007
       37.18589,         0.00009
       37.90012,         0.00006
       38.47604,         0.00009
       39.01364,         0.00007
       39.75864,         0.00007
       40.56032,         0.00007
       41.15358,         0.00007
       41.54546,         0.00006
       42.07594,         0.00006
       42.79207,         0.00005
       43.54447,         0.00005
       44.22097,         0.00005
       44.84709,         0.00005
       45.58542,         0.00008
       46.34520,         0.00006
       46.84686,         0.00005
       47.26888,         0.00006
       47.87076,         0.00009
       48.38534,         0.00005
       49.30737,         0.00005
       49.87582,         0.00007
       50.44976,         0.00005
       51.15869,         0.00007
       51.60122,         0.00005
       52.67146,         0.00006
       52.78433,         0.00005
       53.45289,         0.00006
       54.47275,         0.00008
       55.11775,         0.00008
       55.67943,         0.00006
       56.17295,         0.00006
       56.54980,         0.00006
       57.16567,         0.00006
       58.05508,         0.00008
       58.64530,         0.00005
       59.73103,         0.00006
       59.55753,         0.00006
       60.52892,         0.00006
       61.33431,         0.00006
       61.40680,         0.00005
       62.17889,         0.00005
       62.72137,         0.00006

Process finished with exit code 0

我们可以看到右边这一列基本没什么变化,左边这一列呢基本上都是一个线性的方式增长上去的,我们简单画个图
在这里插入图片描述
我们可以看到pop()是平坦的常数,而pop(0)存在线性增长的趋势,当时,也会有额外几个小点跳出去,这些都是正常的,因为多用户的操作的情况下,会存在许多极其特殊的情况,这都是可以解释的。

字典与列表不同,根据关键码(key)找到数据项,而列表是根据位置(index)最常用的取值get和赋值set,其性能为O(1),另一个重要的操作contains(in)是判断字典中是否存在某个关键码(key)是否存在,这个性能也是O(1)
list和dict的in操作对比,我们同样也可以设计一个性能试验来验证list中检索一个值以及dict中检索一个值的计时对比
我们也会得出结论:
字典的执行时间与规模无关,是常数
列表的执行时间则随着列表的规模加大而线性上升
python官方的算法复杂度网站https://wiki.python.org/moin/TimeComplexity

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1319855.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

微服务实战系列之ZooKeeper(实践篇)

前言 关于ZooKeeper,博主已完整的通过庖丁解牛式的“解法”,完成了概述。我想掌握了这些基础原理和概念后,工作的问题自然迎刃而解,甚至offer也可能手到擒来,真实一举两得,美极了。 为了更有直观的体验&a…

Spark基础入门

spark基础入门 环境搭建 localstandlonespark ha spark code spark corespark sqlspark streaming 环境搭建 准备工作 创建安装目录 mkdir /opt/soft cd /opt/soft下载scala wget https://downloads.lightbend.com/scala/2.13.12/scala-2.13.12.tgz -P /opt/soft解压scala…

基于 Flink 构建实时数据湖的实践

本文整理自火山引擎云原生计算研发工程师王正和闵中元在本次 CommunityOverCode Asia 2023 数据湖专场中的《基于 Flink 构建实时数据湖的实践》主题演讲。 实时数据湖是现代数据架构的核心组成部分,随着数据湖技术的发展,用户对其也有了更高的需求&…

Mysql高可用|索引|事务 | 调优

前言 「作者主页」:雪碧有白泡泡 「个人网站」:雪碧的个人网站 文章目录 前言sql语句的执行顺序关键词连接名字解释sql语句 面试坑点存储引擎MYSQL存储引擎 SQL优化索引索引失效索引的数据结构面试坑点 锁事务四大特性事务的隔离级别MVCC 读写分离面试坑…

以低成本实现高转化:搭建年入百万的知识付费网站的技巧与方法

明理信息科技知识付费平台 一、引言 随着知识经济的崛起,越来越多的知识提供者希望搭建自己的知识付费平台。然而,对于新手来说,如何以低成本、高效率地实现这一目标,同时满足自身需求并提高客户转化率,是一大挑战。…

POST:http://XXX:XXXX/XXXX/XXXX(404 Not found)离谱

很离谱,同样的请求方式,不同的接口会有404的问题。看下边: 上边接口访问正常,下边接口出现404.且本地测试也可以,代码也推到公司git上了。真的很离谱。 我也不知道怎么回事,无语||||||| 哪位兄弟知道啊&a…

4.配置系统时钟思路及方法

前言: 比起之前用过的三星的猎户座4412芯片,STM32F4的系统时钟可以说是小巫见大巫,首先我们需要清晰时钟产生的原理:几乎大多数的芯片都是由晶振产生一个比较低频的频率,然后通过若干个PLL得到单片机能承受的频率&…

2023_Spark_实验二十八:Flume部署及配置

实验目的:熟悉掌握Flume部署及配置 实验方法:通过在集群中部署Flume,掌握Flume配置 实验步骤: 一、Flume简介 Flume是一种分布式的、可靠的和可用的服务,用于有效地收集、聚合和移动大量日志数据。它有一个简单灵活…

LibreNMS:从docker出发

引言 LibreNMS 是一个免费开源的网络监控和自动化工具,用于监视网络设备、服务器和应用程序的性能和状态。它提供了一个集中的管理平台,帮助管理员实时监控和管理整个网络基础设施。 以下是 LibreNMS 的一些主要特点和功能: 自动发现&#…

20、清华、杭州医学院等提出:DA-TransUNet,超越TranUNet,深度医学图像分割框架的[皇帝的新装]

前言: 本文由清华电子工程学院、杭州医学院、大阪大学免疫学前沿研究所、日本科学技术高等研究院信息科学学院、东京法政大学计算机与信息科学专业共同作者,于2023年11月14号发表于arXiv的《Electrical Engineering and Systems Science》期刊。 论文&…

【Python基础】生成器

文章目录 [toc]什么是生成器生成器示例生成器工作流程生成器表达式send()方法和close方法send()方法close()方法 什么是生成器 在Python中,使用生成器可以很方便地支持迭代器协议生成器通过生成器函数产生,通过def定义,但不是通过return返回…

酷雷曼再获“国家高新技术企业”认定

2023年12月8日,《对湖北省认定机构2023年认定报备的第五批高新技术企业拟进行备案的公示》正式发布,酷雷曼武汉同创蓝天科技有限公司成功获评“国家高新技术企业”认定。 屡获权威认定,见证硬核实力 被评定为高新技术企业是我国企业最高荣誉…

武汉小程序开发全攻略:从创意到上线,10个必备步骤详解

在当前数字化时代,小程序已经成为企业营销和服务的重要工具。特别是在武汉这样的创新型城市,小程序开发更是备受青睐。本文将为您详细解读武汉小程序开发的全攻略,从创意到上线的10个必备步骤。 步骤一:确定小程序类型和功能定位…

DSP捕获输入简单笔记

之前使用stm32的大概原理是: 输入引脚输入一个脉冲,捕获1开始极性捕获,捕获的是从启动捕获功能开始计数,捕获的是当前的计数值; 例如一个脉冲,捕获1捕获上升沿,捕获2捕获下降沿;而两…

mysql自动安装脚本(快速部署mysql)

mysql_install - 适用于生产环境单实例快速部署 MySQL8.0 自动安装脚本 mysql8_install.sh(执行前修改一下脚本里的配置参数,改成你自己的)(博客末尾) my_test.cnf(博客末尾)(这个…

Linux性能优化常做的一些事情

Linux性能优化是一个广泛的主题,涉及多个方面。以下是一些常见的Linux性能优化建议: 硬件和系统配置: 使用SSD替代HDD。确保系统有足够的RAM。使用多核CPU。配置合适的网络硬件和带宽。 磁盘I/O性能: 使用RAID来提高I/O性能。使用…

WordCloud—— 词云

【说明】文章内容来自《机器学习入门——基于sklearn》,用于学习记录。若有争议联系删除。 wordcloud 是python的第三方库,称为词云,也成文字云,可以根据文本中的词频以直观和艺术化的形式展示文本中词语的重要性。 依赖于pillow …

物联网对接使用蓝牙还是WiFi,应该如何选择?

蓝牙是一种无线技术协议,可促进连接设备之间短距离的数据交换。它依赖于物理邻近性并使用2.400至2.485 GHz之间的UHF(超高频)无线电波。蓝牙旨在创建个人区域网络(PAN)并在笔记本电脑、智能手机和外围设备等计算设备之…

虚幻学习笔记18—C++委托(多播)和事件

一、前言 委托分单播和多播,多播就是可以绑定多个回调函数,然后一次性执行。这样也可以理解为啥多播没有返回值,多个回调函数执行后返回哪一个都是问题啊。而事件呢官方官方文档说法是“对于事件而言,只有定义事件的类才能调用 Br…

055:vue工具 --- 人民币小写转化为大写

第055个 查看专栏目录: VUE ------ element UI 专栏目标 在vue和element UI联合技术栈的操控下,本专栏提供行之有效的源代码示例和信息点介绍,做到灵活运用。 (1)提供vue2的一些基本操作:安装、引用,模板使…