優先度キュー
要素に優先度を付与し、優先度の高い要素から取り出すデータ構造
データ構造アルゴリズム
優先度キューとは
優先度キュー (Priority Queue) は、各要素に優先度を付与し、優先度の最も高い (または低い) 要素から取り出すデータ構造である。通常のキュー (FIFO) が挿入順に取り出すのに対し、優先度キューは優先度順に取り出す。内部的にはヒープ (Heap) で実装されることが多い。
計算量
| 操作 | 計算量 |
|---|---|
| 挿入 (enqueue) | O(log n) |
| 最高優先度の取り出し (dequeue) | O(log n) |
| 最高優先度の参照 (peek) | O(1) |
TypeScript での実装
class MinHeap<T> {
private heap: { priority: number; value: T }[] = [];
enqueue(value: T, priority: number): void {
this.heap.push({ priority, value });
this.bubbleUp(this.heap.length - 1);
}
dequeue(): T | undefined {
if (this.heap.length === 0) return undefined;
const min = this.heap[0];
const last = this.heap.pop()!;
if (this.heap.length > 0) {
this.heap[0] = last;
this.bubbleDown(0);
}
return min.value;
}
private bubbleUp(i: number): void {
while (i > 0) {
const parent = Math.floor((i - 1) / 2);
if (this.heap[parent].priority <= this.heap[i].priority) break;
[this.heap[parent], this.heap[i]] = [this.heap[i], this.heap[parent]];
i = parent;
}
}
private bubbleDown(i: number): void {
const n = this.heap.length;
while (true) {
let smallest = i;
const left = 2 * i + 1, right = 2 * i + 2;
if (left < n && this.heap[left].priority < this.heap[smallest].priority) smallest = left;
if (right < n && this.heap[right].priority < this.heap[smallest].priority) smallest = right;
if (smallest === i) break;
[this.heap[smallest], this.heap[i]] = [this.heap[i], this.heap[smallest]];
i = smallest;
}
}
}
実務での活用
SQS でのメッセージ優先度
SQS 標準キューには優先度の概念がない。優先度を実現するには、優先度ごとに別のキューを作成し、高優先度キューから先に処理する。
// 高優先度キューを先にポーリング
const highPriority = await sqs.receiveMessage({ QueueUrl: HIGH_QUEUE });
if (highPriority.Messages?.length) {
await processMessages(highPriority.Messages);
} else {
const lowPriority = await sqs.receiveMessage({ QueueUrl: LOW_QUEUE });
await processMessages(lowPriority.Messages ?? []);
}
タスクスケジューリング
OS のプロセススケジューラ、ネットワークパケットの QoS、ジョブキューの優先度制御に使われる。
ダイクストラ法
最短経路アルゴリズム (ダイクストラ法) は、優先度キューを使って「次に探索するノード」を効率的に選択する。
通常のキューとの使い分け
| データ構造 | 取り出し順 | 用途 |
|---|---|---|
| キュー (FIFO) | 挿入順 | メッセージキュー、タスクキュー |
| スタック (LIFO) | 逆挿入順 | 関数呼び出し、Undo |
| 優先度キュー | 優先度順 | スケジューリング、最短経路 |
さらに掘り下げるなら関連書籍が参考になる。