转载来源:https://blog.csdn.net/Baoli1008/article/details/48059623
Python内置方法的时间复杂度
本文翻译自Python Wiki
本文基于GPL v2协议,转载请保留此协议。
本页面涵盖了Python中若干方法的时间复杂度(或者叫“大欧”,“Big O”)。该时间复杂度的计算基于当前(译注:至少是2011年之前)的CPython实现。其他Python的实现(包括老版本或者尚在开发的CPython实现)可能会在性能表现上有些许小小的差异,但一般不超过一个O(log n)项。
本文中,’n’代表容器中元素的数量,’k’代表参数的值,或者参数的数量。
一、列表(list
)
以完全随机的列表考虑平均情况。
列表是以数组(Array)实现的。最大的开销发生在超过当前分配大小的增长,这种情况下所有元素都需要移动;或者是在起始位置附近插入或者删除元素,这种情况下所有在该位置后面的元素都需要移动。如果你需要在一个队列的两端进行增删的操作,应当使用collections.deque
(双向队列)
col 1 | col 2 | col 3 |
---|---|---|
操作 | 平均情况 | 最坏情况 |
复制copy() | O(n) | O(n) |
append(item) [注1] | O(1) | O(1) |
插入元素:insert(i, item) | O(n) | O(n) |
取元素list[i] | O(1) | O(1) |
更改元素list[i] | O(1) | O(1) |
删除元素:pop(i) | O(n) | O(n) |
删除最后一个元素:pop() | O(1) | O(1) |
遍历 | O(n) | O(n) |
取切片 | O(k) | O(k) |
删除切片 | O(n) | O(n) |
更改切片 | O(k+n) | O(k+n) |
extend[注1] | O(k) | O(k) |
排序(Timsort) | O(n log n) | O(n log n) |
列表乘法 | O(nk) | O(nk) |
x in s | O(n) | |
min(s), max(s) | O(n) | |
计算长度 len() | O(1) | O(1) |
二、双向队列(collections.deque
)
deque (double-ended queue,双向队列)是以双向链表的形式实现的 (Well, a list of arrays rather than objects, for greater efficiency)。双向队列的两端都是可达的,但从查找队列中间的元素较为缓慢,增删元素就更慢了。
col 1 | col 2 | col 3 |
---|---|---|
操作 | 平均情况 | 最坏情况 |
复制 | O(n) | O(n) |
append | O(1) | O(1) |
appendleft | O(1) | O(1) |
pop | O(1) | O(1) |
popleft | O(1) | O(1) |
extend | O(k) | O(k) |
extendleft | O(k) | O(k) |
rotate | O(k) | O(k) |
remove | O(n) | O(n) |
三、集合(set
)
内部实现是dict的。在in操作上是O(1), 这一点比list要强。
未列出的操作可参考 dict —— 二者的实现非常相似。
col 1 | col 2 | col 3 |
---|---|---|
操作 | 平均情况 | 最坏情况 |
x in s | O(1) | O(n) |
并集 s | t | O(len(s)+len(t)) |
交集 s&t | O(min(len(s), len(t)) | O(len(s) * len(t)) |
差集 s-t | O(len(s)) | |
s.difference_update(t) | O(len(t)) | |
对称差集 s^t | O(len(s)) | O(len(s) * len(t)) |
s.symmetric_difference_update(t) | O(len(t)) | O(len(t) * len(s)) |
删除元素remove(item) | O(1) | O(n) |
插入元素 add(item) | O(1) | O(n) |
由源码得知,求差集(s-t
,或s.difference(t)
)运算与更新为差集(s.difference_uptate(t)
)运算的时间复杂度并不相同!前者是将在s中,但不在t中的元素添加到新的集合中,因此时间复杂度为O(len(s));后者是将在t中的元素从s中移除,因此时间复杂度为O(len(t))。因此,使用时请留心,根据两个集合的大小以及是否需要新集合来选择合适的方法。
集合的s-t运算中,并不要求t也一定是集合。只要t是可遍历的对象即可。
四、字典(dict
)
下列字典的平均情况基于以下假设:
- 对象的散列函数足够撸棒(robust),不会发生冲突。
- 字典的键是从所有可能的键的集合中随机选择的。
小窍门:只使用字符串作为字典的键。这么做虽然不会影响算法的时间复杂度,但会对常数项产生显著的影响,这决定了你的一段程序能多快跑完。
col 1 | col 2 | col 3 |
---|---|---|
操作 | 平均情况 | 最坏情况 |
复制copy() [注2] | O(n) | O(n) |
插入元素 dict[k] | O(1) | O(n) |
取元素get(k) | O(1) | O(n) |
key in dict | O(1) | O(n) |
dict[k] 更改元素 [注1] | O(1) | O(n) |
删除元素pop(k) | O(1) | O(n) |
keys(), values, items() 遍历[注2] | O(n) | O(n) |
五、堆(heap
)
import heapq
Python 3的heapq
建立的是最小堆;若想建立最大堆,可以在插入数据之前,先把数据取反(即乘上 -1),在弹出数据之后,再把数据取反(即乘上 -1)即可。
heap = [-3, -1, 5, 6, 3]
col 1 | col 2 | col 3 |
---|---|---|
操作 | 平均情况 | 最坏情况 |
复制copy() [注2] | O(n) | O(n) |
建堆heapq.heapify(heap) | O(n) | O(n) |
插入元素 heapq.heappush(heap, item) | O(logn) | O(logn) |
返回根元素heap[0] | O(1) | O(1) |
返回并删除根元素heapq.heappop(heap) | O(1) | O(1) |
heapq.heappushpop(heap, item) 将 item 放入堆中,然后弹出并返回 heap 的最小元素。该组合操作比先调用 heappush() 再调用 heappop() 运行起来更有效率。
注:
[1] = These operations rely on the “Amortized” part of “Amortized Worst Case”. Individual actions may take surprisingly long, depending on the history of the container.
[2] = For these operations, the worst case n is the maximum size the container ever achieved, rather than just the current size. For example, if N objects are added to a dictionary, then N-1 are deleted, the dictionary will still be sized for N objects (at
least) until another insertion is made.