Pythonのdequeでキュー・スタック・両端キューを効率的に扱う方法

 

Pythonのcollectionsモジュールのdeque(デック)は、両端での高速な追加・削除が可能なデータ構造です。キュー、スタック、両端キューとして活用でき、リストよりも効率的な操作を実現できます。

dequeとは

deque(double-ended queue)は、両端で要素の追加・削除がO(1)で実行できるデータ構造です。通常のリストでは先頭への挿入がO(n)ですが、dequeなら高速に処理できます。

from collections import deque

# dequeの作成
dq = deque([1, 2, 3])
print(dq)  # deque([1, 2, 3])

キューとしての使用

FIFOでの要素処理を効率的に実装:

from collections import deque

# キューの実装
queue = deque()

# 要素の追加(enqueue)
queue.append('A')
queue.append('B')
queue.append('C')
print(queue)  # deque(['A', 'B', 'C'])

# 要素の取り出し(dequeue)
item = queue.popleft()
print(item)   # A
print(queue)  # deque(['B', 'C'])

スタックとしての使用

LIFOでの要素処理:

from collections import deque

# スタックの実装
stack = deque()

# 要素のプッシュ
stack.append('X')
stack.append('Y')
stack.append('Z')
print(stack)  # deque(['X', 'Y', 'Z'])

# 要素のポップ
item = stack.pop()
print(item)   # Z
print(stack)  # deque(['X', 'Y'])

両端キューとしての使用

両端での操作を活用:

from collections import deque

# 両端キューの実装
dq = deque(['B', 'C'])

# 左端に追加
dq.appendleft('A')
print(dq)  # deque(['A', 'B', 'C'])

# 右端に追加
dq.append('D')
print(dq)  # deque(['A', 'B', 'C', 'D'])

# 左端から削除
left_item = dq.popleft()
print(left_item)  # A

# 右端から削除
right_item = dq.pop()
print(right_item)  # D
print(dq)  # deque(['B', 'C'])

便利なメソッド

rotate(回転)

from collections import deque

dq = deque([1, 2, 3, 4, 5])

# 右に2つ回転
dq.rotate(2)
print(dq)  # deque([4, 5, 1, 2, 3])

# 左に1つ回転
dq.rotate(-1)
print(dq)  # deque([5, 1, 2, 3, 4])

extend(複数要素の追加)

from collections import deque

dq = deque([1, 2])

# 右端に複数要素追加
dq.extend([3, 4, 5])
print(dq)  # deque([1, 2, 3, 4, 5])

# 左端に複数要素追加
dq.extendleft(['A', 'B'])
print(dq)  # deque(['B', 'A', 1, 2, 3, 4, 5])

maxlenによる固定長deque

循環バッファとして使用:

from collections import deque

# 最大3要素のdeque
circular = deque(maxlen=3)

# 要素を追加
for i in range(5):
    circular.append(i)
    print(f"追加後: {circular}")

# 出力:
# 追加後: deque([0], maxlen=3)
# 追加後: deque([0, 1], maxlen=3)
# 追加後: deque([0, 1, 2], maxlen=3)
# 追加後: deque([1, 2, 3], maxlen=3)
# 追加後: deque([2, 3, 4], maxlen=3)

実践的な活用例

BFS(幅優先探索)

from collections import deque

def bfs(graph, start):
    visited = set()
    queue = deque([start])
    
    while queue:
        node = queue.popleft()
        if node not in visited:
            visited.add(node)
            print(node)
            queue.extend(graph[node] - visited)

# 使用例
graph = {
    'A': {'B', 'C'},
    'B': {'A', 'D'},
    'C': {'A', 'E'},
    'D': {'B'},
    'E': {'C'}
}
bfs(graph, 'A')

履歴管理

from collections import deque

class History:
    def __init__(self, maxlen=10):
        self.history = deque(maxlen=maxlen)
    
    def add(self, item):
        self.history.append(item)
    
    def recent(self, n=5):
        return list(self.history)[-n:]
    
    def undo(self):
        return self.history.pop() if self.history else None

# 使用例
hist = History(maxlen=3)
hist.add("action1")
hist.add("action2")
hist.add("action3")
print(hist.recent())  # ['action1', 'action2', 'action3']

滑動窓の実装

from collections import deque

def sliding_window_max(nums, k):
    dq = deque()
    result = []
    
    for i, num in enumerate(nums):
        # 窓から出る要素を削除
        while dq and dq[0] <= i - k:
            dq.popleft()
        
        # 現在の要素より小さい要素を削除
        while dq and nums[dq[-1]] <= num:
            dq.pop()
        
        dq.append(i)
        
        if i >= k - 1:
            result.append(nums[dq[0]])
    
    return result

# 使用例
nums = [1, 3, -1, -3, 5, 3, 6, 7]
print(sliding_window_max(nums, 3))  # [3, 3, 5, 5, 6, 7]

パフォーマンス比較

listとdequeの性能差:

import time
from collections import deque

# リストでの先頭挿入
def list_insert_test(n):
    lst = []
    start = time.time()
    for i in range(n):
        lst.insert(0, i)
    return time.time() - start

# dequeでの先頭挿入
def deque_insert_test(n):
    dq = deque()
    start = time.time()
    for i in range(n):
        dq.appendleft(i)
    return time.time() - start

n = 10000
list_time = list_insert_test(n)
deque_time = deque_insert_test(n)

print(f"リスト: {list_time:.4f}秒")
print(f"deque:  {deque_time:.4f}秒")

まとめ

dequeは両端での高速操作が必要な場面で威力を発揮するデータ構造です。キュー、スタック、循環バッファとして活用でき、アルゴリズムの効率化に大きく貢献します。

特にBFSや滑動窓アルゴリズム、履歴管理などの実装では、listよりもdequeを選択することで大幅なパフォーマンス向上が期待できます。

■プロンプトだけでオリジナルアプリを開発・公開してみた!!

■AI時代の第一歩!「AI駆動開発コース」はじめました!

テックジム東京本校で先行開始。

■テックジム東京本校

「武田塾」のプログラミング版といえば「テックジム」。
講義動画なし、教科書なし。「進捗管理とコーチング」で効率学習。
より早く、より安く、しかも対面型のプログラミングスクールです。

<短期講習>5日で5万円の「Pythonミニキャンプ」開催中。

<月1開催>放送作家による映像ディレクター養成講座

<オンライン無料>ゼロから始めるPython爆速講座