Python数据结构与算法--List和Dictionaries
Lists
当实现 list 的数据结构的时候Python 的设计者有很多的选择. 每一个选择都有可能影响着 list 操作执行的快慢. 当然他们也试图优化一些不常见的操作. 但是当权衡的时候,它们还是牺牲了不常用的操作的性能来成全常用功能.
本文地址:http://www.cnblogs.com/archimedes/p/python-datastruct-algorithm-list-dictionary.html,转载请注明源地址。
设计者有很多的选择,使他们实现list的数据结构。这些选择可能对如何快速列表操作的影响进行。帮助他们做出正确的选择,他们看着人们最常使用的列表数据结构的方式和他们优化列表的实现,导致最常见的操作速度非常快。当然他们也试图优化不常见的操作,但当一个权衡不得不作一个不太常见的操作的性能往往是牺牲在更常见的操作支持。
两种常见的操作的索引和分配给索引位置。不管列表多大这两个操作所需时间相同。称一个独立于list大小的操作时间复杂度为O(1).
另一个常见的编程操作是增长一个 list. 有两种方法来创建一个更长的list.你可以使用附加尾部的方法或串联运算符。附加的方法是O(1)。然而,连接操作是 O(k) 其中k是需要连接列表的尺寸。这对你很重要,因为它可以帮助你选择正确的工具的工作来使自己的节目更有效。
让我们看一下四种不同的方法构造一个包含 n
个数字起始为 0 的list. Listing 1 展示了list的四种不同的方法实现:
Listing 1
- def test1():
- l = []
- for i in range(1000):
- l = l + [i]
- def test2():
- l = []
- for i in range(1000):
- l.append(i)
- def test3():
- l = [i for i in range(1000)]
- def test4():
- l = list(range(1000))
想要计算每个函数的执行时间, 我们可以使用Python 的 timeit
模块. timeit
模块设计的目的是允许程序员在一致的环境下跨平台的测量时间.
要使用 timeit
你必须先创建一个 Timer
对象,参数为两个Python声明. 第一个参数是你想计算时间是函数声明; 第二个参数是设置测试的次数. timeit
模块将计算执行时间. timeit
默认情况下执行声明参数代表的操作100万次. 当它完成时将返回一个浮点类型的秒数. 然而,因为它执行声明一百万次,你可以将结果理解为每执行一次花费多少毫秒. 你还可以传递给 timeit
函数一个名叫 number
的参数,它可以允许你指定多少次测试语句来执行. 下面显示运行每一个测试函数1000次需要多长时间.
- t1 = Timer("test1()", "from __main__ import test1")
- print("concat ",t1.timeit(number=1000), "milliseconds")
- t2 = Timer("test2()", "from __main__ import test2")
- print("append ",t2.timeit(number=1000), "milliseconds")
- t3 = Timer("test3()", "from __main__ import test3")
- print("comprehension ",t3.timeit(number=1000), "milliseconds")
- t4 = Timer("test4()", "from __main__ import test4")
- print("list range ",t4.timeit(number=1000), "milliseconds")
- concat 6.54352807999 milliseconds
- append 0.306292057037 milliseconds
- comprehension 0.147661924362 milliseconds
- list range 0.0655000209808 milliseconds
上面是实验中,函数声明是 test1()
, test2()
, 等等. 设置的声明会让你感觉很怪, 所以让我们来深入理解一下.你可能很熟悉 from
, import
语句, 但这通常是用在一个Python程序文件开始. 在这种情况下, from __main__ import test1
从 __main__
命名空间将 test1 调入到
timeit
所在的命名空间.
关于这个小实验的最后提到的是, 你看到的关于调用也包含一定的开销时间, 但是我们可以假设, 函数调用的开销在所有四种情况下是相同的, 我们仍然可以得到比较有意义的操作比较结果. 所以不会说串联操作精确地需要6.54毫秒, 而说串联测试函数需要6.54毫秒.
从下表我们可以看到list中所有操作的 Big-O 效率。经过仔细观察,你可能想知道两个不同pop的执行时间的差异。当pop在list的尾部操作需要的时间复杂度为O(1), 当pop在list的头部操作需要的时间复杂度为O(n), 其原因在于Python选择如何实现列表。
操作 | 效率 |
---|---|
index [] | O(1) |
index assignment | O(1) |
append | O(1) |
pop() | O(1) |
pop(i) | O(n) |
insert(i,item) | O(n) |
del operator | O(n) |
iteration | O(n) |
contains (in) | O(n) |
get slice [x:y] | O(k) |
del slice | O(n) |
set slice | O(n+k) |
reverse | O(n) |
concatenate | O(k) |
sort | O(n log n) |
multiply | O(nk) |
为了演示性能上的不同,让我们使用 timeit模块做另一个实验
. 我们的目的是能够证实在一个已知大小的list,从list的尾部和从list的头部上面 pop
操作, 我们还要测量不同list尺寸下的时间. 我们期望的是从list的尾部和从list的头部上面 pop
操作时间是保持常数,甚至当list的大小增加的时候, 然而运行时间随着list的大小的增大而增加.
下面的代码让我们可以区分两种pop操作的执行时间. 就像你看到的那样,在第一个例子中, 从尾部pop操作花费时间为0.0003 毫秒, 然而从首部pop操作花费时间为 4.82 毫秒.
Listing 2
- popzero = timeit.Timer("x.pop(0)",
- "from __main__ import x")
- popend = timeit.Timer("x.pop()",
- "from __main__ import x")
- x = list(range(2000000))
- popzero.timeit(number=1000)
- 4.8213560581207275
- x = list(range(2000000))
- popend.timeit(number=1000)
- 0.0003161430358886719
上面的代码可以看到 pop(0)
确实比 pop()效率低
, 但没有验证 pop(0)
时间复杂度为 O(n) 然而 pop()
为 O(1). 要验证这个我们需要看一个例子同时调用一个list. 看下面的代码:
- popzero = Timer("x.pop(0)",
- "from __main__ import x")
- popend = Timer("x.pop()",
- "from __main__ import x")
- 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))
Dictionaries
Python 第二个主要的数据结构是字典. 你可能记得, 词典不同于列表的是你可以通过关键字而不是位置访问字典中的项. 最重要的是注意获得键和值的操作的时间复杂度是O(1). 另一个重要的字典操作是包含操作. 查看键是否在字典中的操作也为 O(1). 所有的字典操作效率如下表所示:
操作 | 效率 |
---|---|
copy | O(n) |
get item | O(1) |
set item | O(1) |
delete item | O(1) |
contains (in) | O(1) |
iteration | O(n) |
我们最后的性能实验比较了包含了列表和字典之间的操作性能. 在这个过程中我们将证实, 列表包含操作是O(N)词典的是O(1).实验中我们将使用简单的比较. 我们会列出一包含一系列数据的list. 然后, 我们将随机选择数字并查看数据是否在 list中. 如果我们之前的结论正确, 随着list的容量的增大, 所需要的时间也增加.
我们将一个dictionary 包含相同的键做重复的实验. 在这个实验中,我们可以看到, 确定一个数是否在字典中不仅速度快得多, 而且检查的时间甚至不会随着字典容量的增加而改变.
下面的代码实现了这种比较. 注意我们执行相同非操作, number in container
. 不同的是第7行 x
是一个list, 第9行 x
是一个dictionary.
- import timeit
- import random
- for i in range(10000,1000001,20000):
- t = timeit.Timer("random.randrange(%d) in x"%i,
- "from __main__ import random,x")
- x = list(range(i))
- lst_time = t.timeit(number=1000)
- x = {j:None for j in range(i)}
- d_time = t.timeit(number=1000)
- print("%d,%10.3f,%10.3f" % (i, lst_time, d_time))
您还可能感兴趣:
Python数据结构与算法--List和Dictionaries的更多相关文章
- python数据结构与算法
最近忙着准备各种笔试的东西,主要看什么数据结构啊,算法啦,balahbalah啊,以前一直就没看过这些,就挑了本简单的<啊哈算法>入门,不过里面的数据结构和算法都是用C语言写的,而自己对p ...
- Python数据结构与算法--算法分析
在计算机科学中,算法分析(Analysis of algorithm)是分析执行一个给定算法需要消耗的计算资源数量(例如计算时间,存储器使用等)的过程.算法的效率或复杂度在理论上表示为一个函数.其定义 ...
- Python数据结构与算法之图的最短路径(Dijkstra算法)完整实例
本文实例讲述了Python数据结构与算法之图的最短路径(Dijkstra算法).分享给大家供大家参考,具体如下: # coding:utf-8 # Dijkstra算法--通过边实现松弛 # 指定一个 ...
- Python数据结构与算法之图的广度优先与深度优先搜索算法示例
本文实例讲述了Python数据结构与算法之图的广度优先与深度优先搜索算法.分享给大家供大家参考,具体如下: 根据维基百科的伪代码实现: 广度优先BFS: 使用队列,集合 标记初始结点已被发现,放入队列 ...
- python数据结构与算法之问题求解实例
关于问题求解,书中有一个实际的案例. 上图是一个交叉路口的模型,现在问题是,怎么安排红绿灯才可以保证相应的行驶路线互不交错. 第一步,就是把问题弄清楚. 怎么能让每一条行驶路线不冲突呢? 其实,就是给 ...
- python数据结构与算法之问题求解
懂得计算机的童鞋应该都知道,一条计算机程序由数据结构跟算法两大部分组成.所以,其实不管你使用哪种计算机语言编写程序,最终这两部分才是一个程序设计的核心.所以,一个不懂得数据结构与算法的程序员不是一个好 ...
- Python 数据结构和算法
阅读目录 什么是算法 算法效率衡量 算法分析 常见时间复杂度 Python内置类型性能分析 数据结构 顺序表 链表 栈 队列 双端队列 排序与搜索 冒泡排序 选择排序 插入排序 希尔排序 快速排序 归 ...
- Python数据结构与算法(几种排序)
数据结构与算法(Python) 冒泡排序 冒泡排序(英语:Bubble Sort)是一种简单的排序算法.它重复地遍历要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来.遍历数列的工作是 ...
- Python - 数据结构与算法(Data Structure and Algorithms)
入门 The Algorithms Python https://github.com/TheAlgorithms/Python 从基本原理到代码实现的Python算法入门,简洁地展示问题怎样解决,因 ...
随机推荐
- 实现TabView(页签)效果
今天花了点时间,设计了一个网页上用的tabview(页签.tabcontrol)效果.个人觉得实现得比较不错,网页元素用得比较少,js代码也比较精练.测试了一下支持IE.FireFox以及chrome ...
- 固态硬盘寿命实测让你直观SSD寿命!--转
近年来,高端笔记本及系列上网本越来越多的采用固态硬盘来提升整机性能,尽管众所周知固态硬盘除 了在正常的使用中带来更快速度的体验外,还具有零噪音.不怕震动.低功耗等优点,但大家对固态硬盘的寿命问题的担忧 ...
- MongoDB入门一:安装与配置
引言 ——妈妈说名字太长排在后面或在标题堆儿中容易被大家发现. MongoDB的名字来源与单词humongous(极大的,巨大无比的)有关,它是一个可扩展.高性能.开源的NoSQL数据库. 之所以在g ...
- [OpenCV] Image Processing - Fuzzy Set
使用模糊技术进行 (灰度变换Grayscale Transform) 和 (空间滤波Spatial Filtering) 模糊集合为处理不严密信息提供了一种形式. 首先,需要将输入量折算为隶属度,这个 ...
- 小printf的故事(未完待续)
小printf的故事 这篇文章的原文来自:英文原文作者仿照<小王子>中的情节,生动有趣的阐述了小printf从编程小白到专家的成长历程.这是我第一次尝试翻译文章,肯定有很多不足之处,情不要 ...
- 还在抱怨JS文件里没有智能提示吗, VS10以及以上都可以 .NET
1.打开JS文件 2.编写$.我们会发现什么也没有 3.托进来 4.有了哈 保存头部代码新建JS都贴上去.
- ActiveMQ学习(二)——MQ的工作原理
如图所示 首先来看本地通讯的情况,应用程序A和应用程序B运行于同一系统A,它们之间可以借助消息队列技术进行彼此的通讯:应用程序A向队列1发送一条信息,而当应用程序B需要时就可以得到该信息. 其次是远程 ...
- [ASP.NET]谈谈REST与ASP.NET Web API
13天的假期结束,赶紧回来充电了 本节目录 Web API简介 自我寄宿 IIS寄宿 调用Web API Web API原理 Web API简介 REST REST是“REpresentational ...
- 团队项目2.0软件改进分析MathAPP
软件改进分析 在此基础上,进行软件的改进. 首先,我们把这个软件理解成一个投入市场的.帮助小朋友进行算术运算练习的APP. 从质量保证的角度,有哪些需要改进的BUG? 从用户的角度(把自己当成小学生或 ...
- Unity烂笔头1-自定义INSPECTOR属性窗口节点项
1.添加输入框和标签 LevelScript: using UnityEngine; using System.Collections; public class LevelScript : Mono ...