ヒープ・スタック・キュー
要素の追加・削除順序が異なる3つのデータ構造。
ヒープ
なぜヒープが必要か?
ヒープは、常に最小(または最大)要素を素早く取り出せるデータ構造です。優先度キュー、グラフアルゴリズム、ソートアルゴリズムなど、多くの場面で使用されます。特に、要素の追加・削除が頻繁で、常に最小(または最大)要素を取得する必要がある場合に適しています。
いつ使うか?
- 優先度キューを実装する場合
- グラフアルゴリズム(ダイクストラ、プリム法など)を実装する場合
- ソートアルゴリズムを実装する場合
- 常に最小(または最大)要素を取得する必要がある場合
実践テクニック
heapqモジュールを使う
Pythonの `heapq` モジュールは、ヒープ操作のための最適化された実装を提供しています。`heapq.heappush()` や `heapq.heappop()` を使うことで、効率的にヒープ操作を行うことができます。
カスタム比較関数を定義する
ヒープでオブジェクトを扱う場合、カスタム比較関数を定義することで、優先度を制御できます。`__lt__` メソッドを実装することで、オブジェクトの比較ロジックをカスタマイズできます。
計算量を理解する
ヒープの操作の計算量を理解することは、パフォーマンス最適化に重要です。例:要素の追加はO(log n)、最小値の取得はO(1)です。これらを理解して、適切なデータ構造を選択します。
# ヒープの基本操作import heapq
# ヒープの作成heap = []heapq.heappush(heap, 5)heapq.heappush(heap, 3)heapq.heappush(heap, 8)heapq.heappush(heap, 1)print(heap) # [1, 3, 5, 8]
# 最小値の取得print(heapq.heappop(heap)) # 1
# 要素の削除heapq.heappop(heap)print(heap) # [1, 3, 5, 8]スタック
なぜスタックが必要か?
スタックは、最後に追加した要素を最初に取り出すデータ構造です(LIFO)。関数呼び出し履歴の管理、式の評価、バックトラッキング、Undo/Redo機能など、多くの場面で使用されます。特に、後入れ先出し(LIFO)の動作が必要な場合に適しています。
いつ使うか?
- 関数呼び出し履歴を管理する場合
- 式の評価を行う場合
- バックトラッキングを実装する場合
- Undo/Redo機能を実装する場合
実践テクニック
listを使う
Pythonの `list` は、スタックとして使用できます。`append()` で要素を追加し、`pop()` で要素を削除します。`[-1]` でトップ要素を確認できます。
dequeを使う
`collections.deque` は、両端から効率的に要素を追加・削除できる両端キューです。スタックとして使用する場合、`append()` と `pop()` を使います。
計算量を理解する
スタックの操作の計算量を理解することは、パフォーマンス最適化に重要です。例:要素の追加はO(1)、要素の削除はO(1)です。これらを理解して、適切なデータ構造を選択します。
# スタックの基本操作stack = []
# 要素の追加stack.append(1)stack.append(2)stack.append(3)print(stack) # [1, 2, 3]
# 要素の削除(LIFO)print(stack.pop()) # 3print(stack) # [1, 2]
# トップ要素の確認print(stack[-1]) # 2キュー
なぜキューが必要か?
キューは、最初に追加した要素を最初に取り出すデータ構造です(FIFO)。タスクキュー、メッセージキュー、イベント処理、バッファリングなど、多くの場面で使用されます。特に、先入れ先出し(FIFO)の動作が必要な場合に適しています。
いつ使うか?
- タスクキューを実装する場合
- メッセージキューを実装する場合
- イベント処理を実装する場合
- バッファリングを実装する場合
実践テクニック
queueモジュールを使う
Pythonの `queue` モジュールは、FIFOキューの実装を提供しています。ただし、`collections.deque` の方がパフォーマンスが良い場合が多いため、推奨されます。
dequeを使う
`collections.deque` は、両端から効率的に要素を追加・削除できる両端キューです。キューとして使用する場合、`append()` と `popleft()` を使います。スタックとして使用する場合、`append()` と `pop()` を使います。
計算量を理解する
キューの操作の計算量を理解することは、パフォーマンス最適化に重要です。例:要素の追加はO(1)、要素の削除はO(1)です。これらを理解して、適切なデータ構造を選択します。
# キューの基本操作from collections import deque
queue = deque()
# 要素の追加queue.append(1)queue.append(2)queue.append(3)print(queue) # deque([1, 2, 3])
# 要素の削除(FIFO)print(queue.popleft()) # 1print(queue) # deque([2, 3])計算量
ヒープ・スタック・キューの操作の計算量を比較する。
# ヒープの計算量import heapq
heap = []heapq.heappush(heap, 5)heapq.heappush(heap, 3)heapq.heappush(heap, 8)heapq.heappush(heap, 1)
# 最小値の取得: O(1)print(heapq.heappop(heap))
# 要素の追加: O(log n)heapq.heappush(heap, 2)
# 要素の削除: O(log n)heapq.heappop(heap)# スタックの計算量stack = []
# 要素の追加: O(1)stack.append(1)
# 要素の削除: O(1)stack.pop()
# トップ要素の確認: O(1)print(stack[-1])# キューの計算量from collections import deque
queue = deque()
# 要素の追加: O(1)queue.append(1)
# 要素の削除: O(1)queue.popleft()使用例
ヒープ・スタック・キューの実際の使用例を確認する。
# ヒープの使用例# 優先度キューの実装import heapq
class Task: def __init__(self, priority, description): self.priority = priority self.description = description
def __lt__(self, other): return self.priority < other.priority
# タスクの追加tasks = []heapq.heappush(tasks, Task(1, "Low priority task"))heapq.heappush(tasks, Task(3, "Medium priority task"))heapq.heappush(tasks, Task(5, "High priority task"))
# タスクの実行while tasks: task = heapq.heappop(tasks) print(f"Executing: {task.description}") # タスクの処理を行う # ...
# 使用例# タスクを優先度順に実行# スタックの使用例# 関数呼び出しスタックの実装call_stack = []
def process_function(): call_stack.append("start") # 処理を行う call_stack.append("end") if call_stack[-1] == "end": call_stack.pop() # 関数が終了した
# 使用例process_function()process_function()print(call_stack) # []# キューの使用例# タスクキューの実装from collections import deque
task_queue = deque()
# タスクの追加task_queue.append("Task 1")task_queue.append("Task 2")task_queue.append("Task 3")
# タスクの実行while task_queue: task = task_queue.popleft() print(f"Processing: {task}") # タスクの処理を行う # ...
# 使用例# タスクを順番に実行比較
ヒープ・スタック・キューの特徴を比較する。
| 特徴 | ヒープ | スタック | キュー |
|---|---|---|---|
| 要素の追加 | O(log n) | O(1) | O(1) |
| 要素の削除 | O(log n) | O(1) | O(1) |
| 最小/最大要素の取得 | O(1) | O(1) | O(1) |
| 順序 | 優先度順 | LIFO | FIFO |
| 主な用途 | 優先度キュー | 関数呼び出し履歴 | タスクキュー |
合格ライン
演習課題
参考文献
この記事は以下の公的ガイドライン/標準に基づいています。
- heapq Documentation - heapq モジュールドキュメント
- deque Documentation - deque クラスドキュメント
- Python Data Structures - 公式ドキュメント