優先キュー priority_queue
優先キュー priority_queue は、厳密な弱い順序付け基準に従って、最初の要素が含まれる要素の中で最大になるようにデフォルト設定されるコンテナ アダプタです。
プライオリティ キューは、デフォルトでデータを保存するための基礎となるコンテナとしてベクトルを使用し、ヒープ ソート アルゴリズムを使用してベクトル上のデータを並べ替えるため、priority_queue
ヒープになります。注: priority_queue はデフォルトで大きくなります
priority_queue
ファイル内に存在し<queue>
、それを使用するにはパッケージqueue
ファイルのみが必要です
したがって、将来的にヒープを使用する必要がある場合、ヒープを手動で実装する必要はなく、
priority_queue
直接使用できます。
優先キューの定義は次のとおりです。
template <class T, class Container = vector<T>,
class Compare = less<typename Container::value_type> > class priority_queue;
最初のテンプレート パラメータはアダプタ コンテナに配置される要素のタイプです。2
番目のテンプレート パラメータはアダプタで、デフォルト
はベクトルです。3 番目のテンプレート パラメータはファンクタで、デフォルトはlessです。デフォルトが大きな山
関数 | インターフェースの説明 |
---|---|
優先キュー() | 空の優先キューを構築する |
priority_queue (InputIterator が最初、InputIterator が最後) | [first, last) イテレータ範囲を使用して優先キューを構築します |
空の() | 短く言ってください |
上( ) | 優先キュー内の最大 (最小の要素)、つまりヒープの最上位の要素を返します。 |
プッシュ(x) | 要素 x を優先キューに挿入します |
ポップ() | 優先キュー内の最大 (最小) 要素、つまりヒープの最上位要素を削除します。 |
優先キューを使用して試してみましょう。
void test1()
{
priority_queue<int> pq;
pq.push(1);
pq.push(6);
pq.push(9);
pq.push(3);
pq.push(0);
pq.push(2);
while (!pq.empty())
{
cout << pq.top() << " ";
pq.pop();
}//输出 9 6 3 2 1 0
cout << endl;
}
最初に小さなヒープを実装する場合は、3 番目のテンプレート パラメーターを変更するだけで済みます。3 番目のテンプレート パラメーターをgreater<T>
指定したため、2 番目のテンプレート パラメーターも手動で明示的に渡す必要があります。
次に、小さなヒープを実装しますpriority_queue
。
void test1()
{
priority_queue<int,vector<int>,greater<int>> pq;
pq.push(1);
pq.push(6);
pq.push(9);
pq.push(3);
pq.push(0);
pq.push(2);
while (!pq.empty())
{
cout << pq.top() << " ";
pq.pop();
}//输出 0 1 2 3 6 9
cout << endl;
}
priority_queueのシミュレーション実装
プライオリティキューの最下層はヒープソートなので、まずヒープソートの下方調整と上方調整を完了します。
void AdjustDown(int parent)
{
Compare com;
size_t child = 2 * parent + 1;
while (child < _con.size())
{
if (child + 1 < _con.size() && _con[child]<_con[child + 1])
{
child++;
}
if (_con[parent]< _con[child])
{
std::swap(_con[child], _con[parent]);
parent = child;
child = 2 * parent + 1;
}
else
{
break;
}
}
}
void AdjustUp(int child)
{
Compare com;
int parent = (child - 1) / 2;
while (parent >= 0)
{
if (_con[parent]< _con[child])
{
std::swap(_con[parent], _con[child]);
child = parent;
parent = (child - 1) / 2;
}
else
{
break;
}
}
}
プッシュ関数、つまり、最初に要素を基になるアダプターに挿入し、次に上方調整メソッドを呼び出します。
void push(const T& t)
{
_con.push_back(t);
AdjustUp(_con.size() - 1);
}
Pop 関数は、まずアダプター内の最初の要素と末尾の要素を交換し、次に末尾の要素を削除してから、下方調整メソッドを呼び出します。
void pop()
{
std::swap(_con[0], _con[_con.size() - 1]);
_con.pop_back();
AdjustDown(0);
}
実装が非常に簡単な関数もいくつかあります。
bool empty()
{
return _con.empty();
}
const T& top()
{
return _con[0];
}
size_t size()
{
return _con.size();
}
現在の完全なコード:
namespace my_priority_queue
{
template<class T, class Container = std::vector<T>>
class priority_queue
{
private:
void AdjustDown(int parent)
{
size_t child = 2 * parent + 1;
while (child < _con.size())
{
if (child + 1 < _con.size() && _con[child]<_con[child + 1])
{
child++;
}
if (_con[parent]< _con[child])
{
std::swap(_con[child], _con[parent]);
parent = child;
child = 2 * parent + 1;
}
else
{
break;
}
}
}
void AdjustUp(int child)
{
int parent = (child - 1) / 2;
while (parent >= 0)
{
if (_con[parent]< _con[child])
{
std::swap(_con[parent], _con[child]);
child = parent;
parent = (child - 1) / 2;
}
else
{
break;
}
}
}
public:
priority_queue()
{
}
template<class InputIterator>
priority_queue(InputIterator first, InputIterator last)
{
while (first != last)
{
_con.push_back(*first);
first++;
}
for (int i = (_con.size() - 1 - 1) / 2; i >= 0; i--)
{
AdjustDown(i);
}
}
void push(const T& t)
{
_con.push_back(t);
AdjustUp(_con.size() - 1);
}
void pop()
{
std::swap(_con[0], _con[_con.size() - 1]);
_con.pop_back();
AdjustDown(0);
}
bool empty()
{
return _con.empty();
}
const T& top()
{
return _con[0];
}
size_t size()
{
return _con.size();
}
private:
Container _con;
};
}
この時点で、大きなヒープ優先キューを実装しましたが、
小さなヒープ優先キューを実装するにはどうすればよいでしょうか? 現時点では、下方調整と上方調整の比較的大きな部分のみを変更できます。
これが実はかなり面倒なのですが、ラージヒープとスモールヒープの優先キューを秒単位で切り替える方法はあるのでしょうか?
答えは、ファンクターを使用できることです。
ファンクタ
ファンクタは関数オブジェクトとも呼ばれます。このクラスのオブジェクトは関数のように使用でき、クラスを関数のように使用できます。
その実装は、 クラス に関数を実装することですoperator()
。括弧のオーバーロードを使用すると、このクラスのオブジェクトを次のようにすることができます。関数のように使う
ここにファンクターがあります:
class IntLess
{
public:
bool operator()(int x,int y)
{
return x < y;
}
};
ファンクターの使い方 結局のところ、ファンクターはクラスなので、それを使用するときは、最初にオブジェクトをインスタンス化し、
次にこのオブジェクトを function のように使用する必要があります。
オブジェクトの後に追加し()
、必要に応じてパラメーターを渡します。
void test2()
{
IntLess LessFunc;
cout<<LessFunc(1,2)<<endl;//仿函数的使用
}
LessFunc(1,2)
次のように処理されます。LessFunc.operator()(1,2)
次に、ファンクターを使用して優先キューのシミュレートされた実装を改良します。
template<class T>
struct Less//首字母大写,为了与库中的less区分
{
bool operator()(const T& t1, const T& t2)
{
return t1 < t2;
}
};
template<class T>
struct Greater
{
bool operator()(const T& t1, const T& t2)
{
return t1 > t2;
}
};
まず、テンプレート宣言にパラメーターを追加します。
template<class T,class Container = std::vector<T>,class Compare = Less<T>>
次に、上方調整と下方調整の大小判定部分をファンクターに変更します。
void AdjustDown(int parent)
{
Compare com;//仿函数使用前,实例化出一个对象
size_t child = 2 * parent + 1;
while (child < _con.size())
{
if (child + 1 < _con.size() && com(_con[child], _con[child + 1]))
{
child++;
}
if (com(_con[parent], _con[child]))
{
std::swap(_con[child], _con[parent]);
parent = child;
child = 2 * parent + 1;
}
else
{
break;
}
}
}
void AdjustUp(int child)
{
Compare com;//仿函数使用前,实例化出一个对象
int parent = (child - 1) / 2;
while (parent >= 0)
{
if (com(_con[parent], _con[child]))
{
std::swap(_con[parent], _con[child]);
child = parent;
parent = (child - 1) / 2;
}
else
{
break;
}
}
}