[英]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.