いくつかのオブジェクトのpriority_queueがあります:
typedef priority_queue<Object> Queue;
Queue queue;
オブジェクトの1つの優先度が変更される場合があります。キュー内のそのオブジェクトの優先度を、効率的な方法で更新できる必要があります。現在、私はこの方法を使用していますが、これは機能しますが非効率的です。
Queue newQueue;
while (!queue.empty())
{
Object obj=queue.top();
queue.pop();
if (priorityHasChanged(obj))
newQueue.Push_back(Object(new_priority));
else
newQueue.Push_back(obj);
}
newQueue.swap(queue); // this only works because I actually subclassed the priority_queue
// class and exposed a swap method that swaps in the container
当時は急いでいたので、この方法で実装しました。これは、問題なく動作することを確信できる最も迅速な方法でした。しかし、これよりも良い方法が必要です。本当に私が欲しいのは、次のいずれかの方法です。
これを行うための最良の方法は何ですか?
基になるdeque/vector/listなどを取得できないため、標準の優先度キューでは運が悪いと思います。独自に実装する必要があります-それほど難しくはありません。
どちらも実際の更新を実行しませんが、問題を解決するために2つの選択肢を提案できます。
使用 priority_queue
および要素を更新するたびに要素をプッシュします。キューに役に立たないエントリがあるという事実を受け入れます。最上位の値をポップするときは、最新の値が含まれているかどうかを確認してください。そうでない場合は、それを無視して次をポップします。
このようにして、更新された要素の削除を最上位になるまで遅らせます。このアプローチが、ダイクストラアルゴリズムを実現するトッププログラマーによって使用されていることに気づきました。
set
を使用します。また、対数時間で最大の要素を抽出できるようにソートされています。また、古い要素を削除してから再度挿入することもできます。したがって、更新操作はまだ不可能ですが、削除と再挿入は可能です。
両方のアプローチの複雑さは同じようです。
適切なデータ構造は「フィボナッチヒープ」と呼ばれます。ただし、自分で実装する必要があります。挿入/更新はO(1) ExtractMinはO(logn)です
STL(私が知っている)でこれを行う最も簡単な方法は、エントリを削除し、その優先度を更新してから、再挿入することです。 std :: priority_queueを使用すると、これを行うのは非常に遅くなりますが、std :: setを使用して同じことを行うことができます。残念ながら、オブジェクトがセット内にある場合は、オブジェクトの優先度を変更しないように注意する必要があります。
Std :: multimap(優先度から値へのマッピング用)とstd :: map(値から優先度へのマッピング用)を結合するmutable_priority_queueクラスベースを実装しました。これにより、アイテムの挿入/削除、および対数での既存の値の更新が可能になります。時間。あなたはコードとそれを使用する方法の例を得ることができます ここ
高性能の更新可能な優先度キューを実装し、利用できるようにしました github上 。
これはあなたが通常それを使用する方法です:
better_priority_queue::updatable_priority_queue<int,int> pQ;
pQ.Push(0, 30); // or pQ.set(0, 30)
pQ.Push(1, 20);
pQ.Push(2, 10);
pQ.update(2, 25); // or pQ.set(2, 25)
while(!pQ.empty())
std::cout << pQ.pop_value().key << ' ';
// Outputs: 0 2 1
Jarekczekの答えを補足するために、実際にset
と「役に立たないエントリを持つ純粋なヒープ」アプローチの両方が同じ複雑さを持っている場合、stl::set
バージョンは通常stl::priority_queue
バージョンよりもパフォーマンスがはるかに遅くなりますstl::priority_queue
は可能な限り純粋で高速なバイナリヒープであるのに対し、深さが2 * log_2(n_elements)未満であり、定期的な更新が必要な赤黒木で実装されているという事実。これが、ダイクストラを実装するときに通常使用される理由です。
ただし、少数のベースノードで多くの更新を行う場合は、セットアプローチの方が高速になる可能性があります。これは、私のライブラリを使用することで最も改善される場所でもあります。
残念ながら、priority_queueの値を更新することはできません。 priority_queueはそのようなインターフェースを提供しません。
Jarekczekが言ったようにset
を使用するか、 このソリューション (make_heapを使用)を使用する方がよいと思います。
例を使用してreplace_if
を確認することをお勧めします ここ 。