[英]Is removing an element from the front of a list cheap in Python?
我正在编写一个程序,该程序在数据列表的前面或后面进行了大量删除操作,而不是在中间进行删除。
我知道删除最后一个元素很便宜,但是删除第一个元素又如何呢? 例如,假设列表A
的地址为4000
,因此元素0
为4000
,元素1
为4001
。
删除元素0
然后只是使编译器将列表A
的地址放在4001
,还是将4001
元素1
移到4000
的位置,并将所有其他元素下移1
?
不,它并不便宜。 从列表的list.pop(0)
删除元素list.pop(0)
例如,使用list.pop(0)
)是O(N)
操作, 应避免 。 同样,在开头插入元素(使用list.insert(0, <value>)
)同样效率低下。
这是因为在调整列表大小之后,必须更改其元素。 对于CPython,在l.pop(0)
情况下, 这是通过memmove
完成的,而对于l.insert(0, <value>)
, 则通过循环存储的项来实现移位 。
列表是专为快速随机访问和O(1)
对他们的最终操作。
但是,由于通常是执行此操作,因此应考虑使用collections
模块中的deque
(如@ayhan在评论中建议的那样)。 deque
上的文档还强调了list
对象不适合这些操作的方式:
尽管列表对象支持类似的操作,但它们针对快速定长操作进行了优化,并且会为
pop(0)
和insert(0, v)
操作产生O(n)
内存移动成本,这会改变基础数据表示的大小和位置。
(强调我的)
deque
数据结构为两侧(开头和结尾)提供O(1)
复杂性,分别为appendleft
和end使用appendleft
/ popleft
和append
/ pop
方法。
当然,对于较小的列表,这会导致一些额外的空间需求(由于deque
的结构),通常不需要考虑(并且正如@juanpa在评论中指出的那样,并不总是如此),因为列表的大小增长。 最后,正如@ShadowRanger富有洞察力的评论所指出的那样,由于序列大小非常小,因此从前面弹出或插入的问题变得无关紧要,以至于实际上变得无关紧要。
简而言之,对于包含很多项目的列表,如果需要从两侧快速添加/弹出,请使用deque
;否则,如果您要随机访问并追加到末尾,请使用list
。
从Python中的列表开头删除元素是O(n),而从collections的结尾删除元素。deque仅为O(1)。 因此,双端队列对您的目的非常有用,但是应该注意,从双端队列的中间进行访问或添加/删除比列表的开销更大。
删除的O(n)成本是因为CPython中的列表只是实现为一个指针数组,因此您对每个元素的转换成本的直觉是正确的。
这可以在Wiki上的Python TimeComplexity页面中看到。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.