web-dev-qa-db-ja.com

Pythonでリストの先頭から要素を削除するのは簡単ですか?

私はプログラムを書いています。これは、データのリストの前または後ろのいずれかで多くの削除を行いますが、途中ではありません。

最後の要素の削除は安価だと理解していますが、最初の要素の削除はどうですか?たとえば、リストAのアドレスが4000にあり、要素04000にあり、要素14001にあるとします。

要素0を削除してから、コンパイラに4001のリストAのアドレスを追加させるか、または1の要素40014000の位置にシフトし、他のすべての要素を1だけ下にシフトしますか?

24
Abid Rizvi

いいえ、安くはありません。リストの先頭から要素を削除する(list.pop(0)を使用するなど)ことはO(N)操作であり、は避ける必要があります。同様に、(list.insert(0, <value>)を使用して)最初に要素を挿入することも同様に非効率的です。

これは、リストのサイズを変更した後、要素をシフトする必要があるためです。 CPythonの場合、l.pop(0)の場合、 これはmemmove を使用して行われますが、l.insert(0, <value>)の場合は シフトは、格納されたアイテムを介したループで実装されます

リストは、高速ランダムアクセスおよびO(1)操作用にend


ただし、この操作は一般的に行っているので、dequeモジュールの collectionsを使用することを検討する必要があります (@ayhanがコメントで提案したように)。 dequeのドキュメントでは、listオブジェクトがこれらの操作に適していない方法についても説明しています。

リストオブジェクトは同様の操作をサポートしますが、高速固定長操作およびincur O(n)基になるデータ表現のサイズと位置の両方を変更するpop(0)およびinsert(0, v)操作のメモリ移動コスト。

(エンファシス鉱山)

dequeデータ構造は、開始と終了にそれぞれappendleft/popleftおよびappend/popメソッドを使用して、両側(開始と終了)にO(1)の複雑さを提供します。

もちろん、サイズが小さいと、(dequeの構造に起因して)余分なスペース要件が発生します。これは、リストのサイズとして一般に問題にならない(そして@juanpaがコメントに記載しているように、常に保持されるとは限らない)育ちます。最後に、@ ShadowRangerの洞察に満ちたコメントノートのように、シーケンスサイズが非常に小さいと、前面からのポップまたは挿入の問題は簡単になり、本当に問題になることはありません。

つまり、簡単に言うと、多くのアイテムを含むリストで、両側から高速の追加/ポップが必要な場合はdequeを使用し、そうでない場合はランダムにアクセスして末尾に追加する場合はlistsを使用します。

PythonはO(n)のリストの先頭から要素を削除しますが、 collections.deque の末尾から要素を削除するのはO(1)のみです。結果として、両端キューは目的にとって素晴らしいものになりますが、両端キューの途中からアクセスまたは追加/削除すると、リストの場合よりもコストがかかることに注意してください。

O(n)削除のコストは、CPythonのリストはポインタの配列として実装されているだけなので、各要素のシフトコストに関する直感は正しいからです。

これは、Wikiの Python TimeComplexityページ で確認できます。

16
miradulo