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选择如何实现列表。

Python List 操作的效率(Big-O)
操作           效率          
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). 所有的字典操作效率如下表所示:

 Dictionary操作的执行效率(Big-O ) 
操作             效率              
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数据结构与算法--算法分析

Python数据结构与算法--面向对象

Python数据结构与算法--数据类型

Python基础(10)--数字

Python基础(9)--正则表达式

Python基础(8)--文件

Python基础(7)--函数

Python基础(6)--条件、循环

Python基础(5)--字典

Python基础(4)--字符串

Python基础(3)--列表和元组

Python基础(2)--对象类型

Python基础(1)--Python编程习惯与特点

Python数据结构与算法--List和Dictionaries的更多相关文章

  1. python数据结构与算法

    最近忙着准备各种笔试的东西,主要看什么数据结构啊,算法啦,balahbalah啊,以前一直就没看过这些,就挑了本简单的<啊哈算法>入门,不过里面的数据结构和算法都是用C语言写的,而自己对p ...

  2. Python数据结构与算法--算法分析

    在计算机科学中,算法分析(Analysis of algorithm)是分析执行一个给定算法需要消耗的计算资源数量(例如计算时间,存储器使用等)的过程.算法的效率或复杂度在理论上表示为一个函数.其定义 ...

  3. Python数据结构与算法之图的最短路径(Dijkstra算法)完整实例

    本文实例讲述了Python数据结构与算法之图的最短路径(Dijkstra算法).分享给大家供大家参考,具体如下: # coding:utf-8 # Dijkstra算法--通过边实现松弛 # 指定一个 ...

  4. Python数据结构与算法之图的广度优先与深度优先搜索算法示例

    本文实例讲述了Python数据结构与算法之图的广度优先与深度优先搜索算法.分享给大家供大家参考,具体如下: 根据维基百科的伪代码实现: 广度优先BFS: 使用队列,集合 标记初始结点已被发现,放入队列 ...

  5. python数据结构与算法之问题求解实例

    关于问题求解,书中有一个实际的案例. 上图是一个交叉路口的模型,现在问题是,怎么安排红绿灯才可以保证相应的行驶路线互不交错. 第一步,就是把问题弄清楚. 怎么能让每一条行驶路线不冲突呢? 其实,就是给 ...

  6. python数据结构与算法之问题求解

    懂得计算机的童鞋应该都知道,一条计算机程序由数据结构跟算法两大部分组成.所以,其实不管你使用哪种计算机语言编写程序,最终这两部分才是一个程序设计的核心.所以,一个不懂得数据结构与算法的程序员不是一个好 ...

  7. Python 数据结构和算法

    阅读目录 什么是算法 算法效率衡量 算法分析 常见时间复杂度 Python内置类型性能分析 数据结构 顺序表 链表 栈 队列 双端队列 排序与搜索 冒泡排序 选择排序 插入排序 希尔排序 快速排序 归 ...

  8. Python数据结构与算法(几种排序)

    数据结构与算法(Python) 冒泡排序 冒泡排序(英语:Bubble Sort)是一种简单的排序算法.它重复地遍历要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来.遍历数列的工作是 ...

  9. Python - 数据结构与算法(Data Structure and Algorithms)

    入门 The Algorithms Python https://github.com/TheAlgorithms/Python 从基本原理到代码实现的Python算法入门,简洁地展示问题怎样解决,因 ...

随机推荐

  1. Android UI开发第四十篇——ScrollTricks介绍

    ScrollTricks是一个开源控件,实现了两个简单功能: 1.Quick Return:向上滑动时,View也向上滑动并且消失,当向下滑动时,View马上出现.例如Google Now的搜索功能. ...

  2. 利用react native创建一个天气APP

    我们将构建一个实列程序:天气App,(你可以在react native 中创建一个天气应用项目),我们将学习使用并结合可定义模板(stylesheets).盒式布局(flexbox).网络通信.用户输 ...

  3. [Design Patterns] 4. Creation Pattern

    设计模式是一套被反复使用.多数人知晓的.经过分类编目的.代码设计经验的总结,使用设计模式的目的是提高代码的可重用性,让代码更容易被他人理解,并保证代码可靠性.它是代码编制真正实现工程化. 四个关键元素 ...

  4. 在做Android开发的,如何去掉滚动view在尽头时的阴影效果

    不经意的在开发中,发现qq的侧滑几乎没有阴影效果,就是拉到边界没有时出现的效果:于是在网上找了下,发现很简单的设置 只要在xml布局文件的滚动或者侧滑控件中加入如下样式: android:overSc ...

  5. 【Java】一次SpringMVC+ Mybatis 配置多数据源经历

    需求 现在在维护的是学校的一款信息服务APP的后台,最近要开发一些新功能,其中一个就是加入学校电影院的在线购票.在线购票实际上已经有一套系统了,但是是外包给别人开发的,我们拿不到代码只能拿到数据库,并 ...

  6. 受限玻尔兹曼机(RBM)学习笔记(五)梯度计算公式

      去年 6 月份写的博文<Yusuke Sugomori 的 C 语言 Deep Learning 程序解读>是囫囵吞枣地读完一个关于 DBN 算法的开源代码后的笔记,当时对其中涉及的算 ...

  7. python进阶学习笔记(四)--多线程thread

    在使用多线程之前,我们首页要理解什么是进程和线程. 什么是进程? 计算机程序只不过是磁盘中可执行的,二进制(或其它类型)的数据.它们只有在被读取到内存中,被操作系统调用的时候才开始它们的生命期.进程( ...

  8. [git]查看某一行代码是谁写的

    git blame filename,'blame'意思为责怪!哈哈哈. 就会列出来每行的修改纪录.你可以通过行数或者代码来查看,是谁的锅!

  9. SQL Server 2016里TempDb的提升

    几个星期前,SQL Server 2016的最新CTP版本已经发布了:CTP 2.4(目前已经是CTP 3.0).这个预览版相比以前的CTP包含了很多不同的提升.在这篇文章里我会谈下对于SQL Ser ...

  10. SQL Server里的INTERSECT ALL

    在上一篇文章里,我讨论了INTERSECT设置操作的基础,它和INNER JOIN的区别,还有为什么需要好的索引设计支持.今天我想谈下SQL Server里并未实现的INTERSECT ALL操作. ...