priority_queueとは
C++のSTL(Standard Template Library)に含まれるpriority_queue
は、データを優先度順に管理するデータ構造です。内部的にはヒープというデータ構造を用いて実装されており、要素の挿入と削除がどちらも対数時間で行えます。
priority_queue
は、デフォルトでは最大値が先頭にくるように要素を管理します。つまり、priority_queue
の先頭には常に最大の要素があり、pop
操作を行うと最大の要素が削除されます。これは、priority_queue
が最大ヒープとして実装されているためです。
しかし、比較関数をカスタマイズすることで、最小値が先頭にくる最小ヒープとしてpriority_queue
を使用することも可能です。これについては後のセクションで詳しく説明します。
次に、priority_queue
の基本的な使い方について見ていきましょう。以下に、priority_queue
を用いて数値を管理する簡単なコードを示します。
#include <queue>
#include <iostream>
int main() {
std::priority_queue<int> pq;
// 要素の挿入
pq.push(3);
pq.push(5);
pq.push(1);
// 要素の取り出し
while (!pq.empty()) {
std::cout << pq.top() << std::endl; // 先頭の要素を表示
pq.pop(); // 先頭の要素を削除
}
return 0;
}
このコードを実行すると、5
, 3
, 1
という順番で数値が表示されます。これは、priority_queue
が最大値から順に要素を管理しているためです。この性質を利用することで、データを優先度順に処理するさまざまな問題を効率的に解くことができます。具体的な応用例については、後のセクションで紹介します。
pairの基本的な使い方
C++のSTLには、2つの値を一組として扱うためのpair
という便利なクラスがあります。pair
はテンプレートクラスであり、任意の型の値をペアとして保持することができます。
pair
の基本的な使い方は非常にシンプルです。以下に、pair
を用いて2つの数値を一組として管理する簡単なコードを示します。
#include <utility>
#include <iostream>
int main() {
std::pair<int, int> p;
// 値の設定
p.first = 1;
p.second = 2;
// 値の取り出し
std::cout << "first: " << p.first << ", second: " << p.second << std::endl;
return 0;
}
このコードを実行すると、first: 1, second: 2
という出力が得られます。pair
のfirst
とsecond
メンバに直接アクセスすることで、ペアの各要素を取り出すことができます。
また、pair
のコンストラクタを用いて、ペアを作成する際に値を設定することも可能です。以下にその例を示します。
#include <utility>
#include <iostream>
int main() {
// 値の設定とペアの作成
std::pair<int, int> p(1, 2);
// 値の取り出し
std::cout << "first: " << p.first << ", second: " << p.second << std::endl;
return 0;
}
このコードも同様に、first: 1, second: 2
という出力を得ます。
pair
は、2つの値を一組として扱いたいときに非常に便利です。例えば、座標を表すためにpair<int, int>
を用いることができます。また、異なる型の値を一組として扱いたい場合にもpair
を用いることができます。例えば、文字列とその長さを一組として扱うためにpair<std::string, int>
を用いることができます。
次に、priority_queue
とpair
を組み合わせて使用する方法について見ていきましょう。
greater関数オブジェクトとの組み合わせ
C++のSTLには、比較を行うための関数オブジェクトとしてgreater
が用意されています。greater
は、その名の通り、大きい方を優先する比較を行います。これをpriority_queue
と組み合わせることで、最小値が先頭にくる最小ヒープを実現することができます。
また、pair
と組み合わせることで、ペアの要素を基に優先度を決定することも可能です。pair
の比較は、第一要素を優先して行われ、第一要素が同じ場合には第二要素が比較されます。これを利用することで、複雑な優先度を持つデータを効率的に管理することができます。
以下に、priority_queue
とpair
、greater
を組み合わせたコードを示します。
#include <queue>
#include <iostream>
int main() {
// 最小値が先頭にくるpriority_queue(最小ヒープ)
std::priority_queue<int, std::vector<int>, std::greater<int>> pq;
// 要素の挿入
pq.push(3);
pq.push(5);
pq.push(1);
// 要素の取り出し
while (!pq.empty()) {
std::cout << pq.top() << std::endl; // 先頭の要素を表示
pq.pop(); // 先頭の要素を削除
}
return 0;
}
このコードを実行すると、1
, 3
, 5
という順番で数値が表示されます。これは、priority_queue
が最小値から順に要素を管理しているためです。
次に、pair
とgreater
を組み合わせた例を見てみましょう。
#include <queue>
#include <iostream>
int main() {
// pairの第一要素を優先して最小値が先頭にくるpriority_queue
std::priority_queue<std::pair<int, int>, std::vector<std::pair<int, int>>, std::greater<std::pair<int, int>>> pq;
// 要素の挿入
pq.push(std::make_pair(3, 2));
pq.push(std::make_pair(1, 5));
pq.push(std::make_pair(3, 1));
// 要素の取り出し
while (!pq.empty()) {
std::cout << "first: " << pq.top().first << ", second: " << pq.top().second << std::endl;
pq.pop();
}
return 0;
}
このコードを実行すると、以下のような出力が得られます。
first: 1, second: 5
first: 3, second: 1
first: 3, second: 2
これは、priority_queue
がpair
の第一要素を優先して最小値から順に要素を管理しているためです。第一要素が同じ場合には、第二要素が比較されます。
以上が、priority_queue
とpair
、greater
を組み合わせた使用方法の基本です。これらを組み合わせることで、様々な優先度を持つデータを効率的に管理することができます。
具体的なコード例
以下に、priority_queue
とpair
、greater
を組み合わせて使用する具体的なコード例を示します。このコードは、各タスクの優先度とタスク名を管理するシンプルなタスクスケジューラを実装したものです。
#include <queue>
#include <iostream>
#include <string>
int main() {
// 優先度とタスク名をペアとして管理するpriority_queue
// 優先度が小さいほど先に処理される(優先度が同じ場合は、タスク名の辞書順)
std::priority_queue<std::pair<int, std::string>, std::vector<std::pair<int, std::string>>, std::greater<std::pair<int, std::string>>> tasks;
// タスクの追加
tasks.push(std::make_pair(3, "Task 1"));
tasks.push(std::make_pair(1, "Task 2"));
tasks.push(std::make_pair(2, "Task 3"));
// タスクの処理
while (!tasks.empty()) {
std::cout << "Processing " << tasks.top().second << " with priority " << tasks.top().first << std::endl;
tasks.pop();
}
return 0;
}
このコードを実行すると、以下のような出力が得られます。
Processing Task 2 with priority 1
Processing Task 3 with priority 2
Processing Task 1 with priority 3
これは、priority_queue
がpair
の第一要素(優先度)を優先して最小値から順に要素を管理しているためです。優先度が同じ場合には、第二要素(タスク名)が比較されます。
以上が、priority_queue
とpair
、greater
を組み合わせた具体的なコード例です。これらを組み合わせることで、様々な優先度を持つデータを効率的に管理することができます。
まとめ
この記事では、C++のSTLに含まれるpriority_queue
とpair
、そしてgreater
関数オブジェクトを組み合わせた使用方法について詳しく説明しました。
まず、priority_queue
はデータを優先度順に管理するデータ構造であり、内部的にはヒープというデータ構造を用いて実装されています。デフォルトでは最大値が先頭にくるように要素を管理しますが、比較関数をカスタマイズすることで最小値が先頭にくる最小ヒープとしても使用可能です。
次に、pair
は2つの値を一組として扱うためのクラスで、任意の型の値をペアとして保持することができます。pair
の比較は第一要素を優先して行われ、第一要素が同じ場合には第二要素が比較されます。
そして、greater
はその名の通り、大きい方を優先する比較を行う関数オブジェクトです。これをpriority_queue
と組み合わせることで、最小値が先頭にくる最小ヒープを実現することができます。
以上の組み合わせを利用することで、様々な優先度を持つデータを効率的に管理することができます。具体的なコード例を通じて、これらの使用方法を理解することができたことでしょう。
C++のSTLは非常に強力で、これらの機能を活用することで多くの問題を効率的に解決することができます。今後もC++のさまざまな機能を活用して、より高度なプログラミングを目指していきましょう。