Python | データ構造強化:collections.deque

Python Python
スポンサーリンク

概要(deque は両端から高速に追加・削除できる「双方向キュー」)

collections.deque(デック)は、先頭と末尾のどちら側からでも高速に要素を追加・削除できるデータ構造です。リストが先頭操作に弱いのに対して、deque は両端操作がほぼ常に定数時間で行えるため、キュー(FIFO)やスタック(LIFO)を効率的に実装できます。必要に応じて最大長(maxlen)を設定し、溢れた要素を自動で反対側から捨てるリングバッファとしても使えます。さらに、deque の基本操作は複数スレッドからの同時アクセスでも正しい結果になるよう設計されています。

from collections import deque

dq = deque([1, 2, 3])         # 初期化
dq.append(4)                  # 末尾に追加
dq.appendleft(0)              # 先頭に追加
print(dq)                     # deque([0, 1, 2, 3, 4])
dq.pop()                      # 末尾から取り出し → 4
dq.popleft()                  # 先頭から取り出し → 0
Python

基本の使い方(ここが重要)

初期化と主要メソッド(append/appendleft/pop/popleft/extend)

deque はイテラブルから初期化でき、両端の追加・削除が得意です。まとめて追加する extend/extendleft、回転する rotate、要素数カウントの count など、実用的なメソッドが揃っています。

from collections import deque

dq = deque([1, 2, 3])
dq.append(4)            # → deque([1,2,3,4])
dq.appendleft(0)        # → deque([0,1,2,3,4])
dq.extend([5, 6])       # 末尾へ一括追加
dq.extendleft([-2, -1]) # 先頭へ逆順で一括追加 → [-1,-2,0,1,2,3,4,5,6]
dq.rotate(1)            # 末尾を先頭へ → [6,-1,-2,0,1,2,3,4,5]
print(dq.count(1))      # 出現回数
Python

maxlen(固定長で溢れたら自動で捨てる)

maxlen を指定すると、長さ制限を超えた分を自動的に反対側から削除します。ログバッファやスライディングウィンドウの実装に最適です。

from collections import deque

buf = deque(maxlen=3)
for x in [10, 20, 30, 40]:
    buf.append(x)
    print(list(buf))    # [10], [10,20], [10,20,30], [20,30,40](10が押し出される)
Python

キューとスタック(両端操作が速い=定番データ構造に最適)

キュー(FIFO)を効率的に

先頭から取り出す popleft が高速なので、待ち行列の処理に向きます。リストでの先頭 pop は全体のシフトが発生し遅くなりがちですが、deque はそれを避けられます。

from collections import deque

queue = deque()
queue.append("A")
queue.append("B")
queue.append("C")
print(queue.popleft())  # A(先入れ先出し)
print(queue)            # deque(['B', 'C'])
Python

スタック(LIFO)も自然に書ける

末尾操作が得意なので、push=append、pop=pop で短く書けます。

from collections import deque

stack = deque()
stack.append("X")
stack.append("Y")
stack.append("Z")
print(stack.pop())  # Z(後入れ先出し)
Python

実務で便利なテクニック(スライディングウィンドウ・ログバッファ・BFS)

スライディングウィンドウ(直近 N 件の移動平均など)

maxlen と追加を組み合わせると、常に「最新 N 件」だけを保持できます。時系列処理の基本形です。

from collections import deque

win = deque(maxlen=5)
def moving_avg(xs):
    out = []
    for x in xs:
        win.append(x)
        out.append(sum(win) / len(win))
    return out

print(moving_avg([1,2,3,4,5,6,7]))  # 直近5件で平均
Python

ログバッファ(最新だけ保持してメモリ節約)

大量ログでも直近だけ見たいなら deque(maxlen=…) が定石です。

from collections import deque

logs = deque(maxlen=1000)
def write_log(line: str) -> None:
    logs.append(line)
    # 古い行は自動的に捨てられる
Python

幅優先探索(BFS)の待ち行列

グラフ探索では「先入れ先出し」の待ち行列が必要です。deque の popleft が効きます。

from collections import deque

def bfs(adj, start):
    seen = {start}
    q = deque([start])
    order = []
    while q:
        v = q.popleft()
        order.append(v)
        for w in adj.get(v, []):
            if w not in seen:
                seen.add(w)
                q.append(w)
    return order
Python

重要ポイントの深掘り(性能特性・スレッド安全性・注意点)

両端操作が高速、ランダムアクセスは不得意

deque は両端操作のために設計されており、両端の追加・削除が高性能です。一方で、任意位置の挿入やインデックスアクセスはリストほど向いていません。「両端が主役」な用途で選ぶのがコツです。

スレッドからの同時アクセスに強い

append/pop 系の操作は複数スレッドから同時に行っても正しい結果になるよう設計されています。高頻度でキュー処理を行う並列シナリオにも適しています。

反転・回転・一括追加の使い分け

extend は末尾へそのまま追加、extendleft は先頭へ「逆順」追加、rotate は両端を回転させます。大量の投入・取り出しパターンに合わせて選びましょう。


例題で身につける(定番から一歩先まで)

例題1:先頭・末尾を自在に使う二端キュー

from collections import deque

dq = deque()
dq.append("right-1")
dq.appendleft("left-1")
dq.append("right-2")
dq.appendleft("left-2")
print(dq)            # deque(['left-2','left-1','right-1','right-2'])
print(dq.popleft())  # 'left-2'
print(dq.pop())      # 'right-2'
Python

例題2:固定長ログ(最新100件だけ)

from collections import deque

recent = deque(maxlen=100)
for i in range(150):
    recent.append(f"line {i}")
print(recent[0], recent[-1])  # 最古は 'line 50'、最新は 'line 149'
Python

例題3:スライディング最大値(簡易版)

from collections import deque

def sliding_max(xs, k):
    dq, out = deque(), []
    for i, x in enumerate(xs):
        while dq and dq[0] <= i - k: dq.popleft()   # 範囲外を捨てる
        while dq and xs[dq[-1]] <= x: dq.pop()      # 小さい末尾を捨てる
        dq.append(i)
        if i >= k - 1:
            out.append(xs[dq[0]])
    return out

print(sliding_max([2,1,3,4,6,3,8,9,10,12,56], 3))
Python

例題4:rotate で末尾を先頭に

from collections import deque

dq = deque([1, 2, 3, 4])
dq.rotate(1)    # → [4, 1, 2, 3]
dq.rotate(-2)   # → [2, 3, 4, 1]
print(dq)
Python

まとめ

collections.deque は「両端が主役」の処理に特化したデータ構造で、キューやスタック、固定長バッファ、スライディングウィンドウに抜群の適性があります。両端の追加・削除が高速で、maxlen による自動押し出しや rotate/extend/extendleft といった豊富な操作が現場のニーズに直結します。リストで先頭操作に苦戦しているなら、deque に切り替えるだけでコードは短く、性能は安定し、運用が楽になります。

タイトルとURLをコピーしました