ヒープを作成するために使用したいベクトルがあります。 C++ make_heap関数を使用する必要があるのか、ベクターを優先キューに入れる必要があるのかわかりませんか?パフォーマンスの点でどちらが優れていますか?どちらを使用する必要がありますか?
パフォーマンスのサームに違いはありません。 _std::priority_queue
_は、コンテナとまったく同じヒープ関連の関数呼び出しをクラスにラップする単なるアダプタクラスです。 _std::priority_queue
_の仕様は、それを公然と述べています。
公開された_std::vector
_からヒープベースの優先度キューを構築することにより(ヒープ関連の関数を直接呼び出すことにより)、外部アクセスの可能性に対してオープンに保ち、ヒープ/キューの整合性を損なう可能性があります。 _std::priority_queue
_は、「正規の」最小値へのアクセスを制限するバリアとして機能します:Push()
、pop()
、top()
など。 -規律施行措置。
また、キューインターフェイスを「標準的な」一連の操作に適合させることで、同じ外部仕様に準拠する優先キューの他のクラスベースの実装と統一して互換性を持たせることができます。
Priority_queueは(少なくとも通常は)ヒープとして実装されます。そのため、本当の問題は、priority_queueが必要なものを提供するかどうかです。 make_heapを使用する場合でも、すべての要素にアクセスできます。 priority_queueを使用する場合、要素へのアクセスを非常に制限する操作はわずかです(基本的には、アイテムを挿入し、キューの先頭にあるアイテムを削除するだけです)。
C++ 11標準
C++ 11 N3337標準ドラフト 「23.6.4.1priority_queueコンストラクター」のstd::make_heap
のコンストラクターでstd::priority_queue
が使用されることを指定します。
明示的なpriority_queue
2効果:compをxで初期化し、cをyで初期化します(必要に応じて、コピー構築または移動構築)。 make_heap(c.begin()、c.end()、comp)を呼び出します。
そして他の方法は言う:
void Push(const value_type&x);
効果:c.Push_back(x); Push_heap(c.begin()、c.end()、comp)
ただし、新しいn4724では、コンストラクター以外のメソッドの表現が「あたかも」になるため、*_heap
メソッドの実際の呼び出しは保証されず、機能的な動作のみが保証されると思います。
デバッグをg++
6.4 stdlibc ++ソースにステップインして、priority_queue
がmake_heap
に転送されることを確認します。
Ubuntuの16.04のデフォルトのg++-6
パッケージまたは ソースからのGCC 6.4ビルド では、追加のセットアップなしでC++ライブラリにステップインできます。
これを使用すると、std::priority_queue
がstd::make_heap
ファミリの単なるラッパーであり、基礎となるstd::vector
があることを簡単に確認できます。これは、パフォーマンスが同じであることを意味します。
a.cpp:
#include <cassert>
#include <queue>
int main() {
std::priority_queue<int> q;
q.emplace(2);
q.emplace(1);
q.emplace(3);
assert(q.top() == 3);
q.pop();
assert(q.top() == 2);
q.pop();
assert(q.top() == 1);
q.pop();
}
コンパイルとデバッグ:
g++ -g -std=c++11 -O0 -o a.out ./a.cpp
gdb -ex 'start' -q --args a.out
ここで、コンストラクターstd::priority_queue<int> q
に最初にステップインするとvector
コンストラクターに入るので、std::priority_queue
にstd::vector
が含まれていることはすでに推測できます。
ここで、GDBでfinish
を実行してキューコンストラクターを見つけ、もう一度ステップインすると、実際のキューコンストラクター/usr/include/c++/6/bits/stl_queue.h
に移動します。
443 explicit
444 priority_queue(const _Compare& __x = _Compare(),
445 _Sequence&& __s = _Sequence())
446 : c(std::move(__s)), comp(__x)
447 { std::make_heap(c.begin(), c.end(), comp); }
これは明らかに、c
オブジェクトの上にあるstd::make_heap
に転送するだけです。
したがって、ソースファイルをvim
で開き、c
の定義を見つけます。
template<typename _Tp, typename _Sequence = vector<_Tp>,
typename _Compare = less<typename _Sequence::value_type> >
class priority_queue
{
[...]
_Sequence c;
したがって、c
はvector
であると結論付けます。
他のメソッドにステップインするか、ソースをさらに調べると、他のすべてのpriority_queue
メソッドもstd::make_heap
関数ファミリーに転送されることが簡単にわかります。
ヒープの平均挿入時間はヒープの方が短いため、ヒープと言う、バランスの取れたBSTの選択は理にかなっています。以下を参照してください。 ヒープと二分探索木(BST)
_priority_queue
_はコンテナではありません。これは、特定の基盤となるコンテナを使用するコンテナアダプタです。 vector
またはdeque
であり、データを操作するための特定のメソッドセットを提供します。さらに、その実装は_*_heap
_アルゴリズムに依存しています。
たとえば、新しい値をvector
にプッシュするときはいつでも、_Push_heap
_を呼び出して、ヒープと見なされる範囲を拡張する必要があります。 _priority_queue
_を使用しない場合、たとえば、vector
の半分をヒープ(std::make_heap(v.begin(), v.begin() + (v.size() / 2))
)と見なすことができますが、残りの半分は次のようになります-です。
Push
を呼び出すと_priority_queue
_が行うこと:新しい要素を基になるコンテナの後ろにプッシュし、_Push_heap
_を呼び出して、ヒーププロパティの優先順位を維持します(最初の要素のみが重要です)最高になるために)。
パフォーマンスの問題よりも、ソリューションの設計と特定の要件を検討したほうがよいと思います。
make_heapは、例として、ヒープを出力するなど、カプセル化を犠牲にして柔軟性を実現します。
Make_heapの興味深い使用法は、マージの片側でmake_heapを使用して、n/2(log(n/2))の最悪の場合のインプレースマージを実現するインプレースマージソートです。
この例は、入力ベクトルの使用と、作成されたヒープの出力を示しています。
#include <queue>
#include <iostream>
#include <string>
#include <vector>
using namespace std;
void print(string prefix,vector<int>& v)
{
cout << prefix;
for(int i : v)
cout << i << " ";
cout << endl;
}
int main()
{
vector<int> v={1,2,9,0,3,8,4,7,1,2,9,0,3,8,4,7};
typedef priority_queue< int,vector<int>,greater<int> > MinQ;
MinQ minQ(v.begin(),v.end()); //minQ
print("After priority_queue constructor: ",v);
make_heap(v.begin(),v.end(),greater<int>());
print("After make_heap: ", v);
return 0;
}
出力:
After priority_queue constructor: 1 2 9 0 3 8 4 7 1 2 9 0 3 8 4 7
After make_heap: 0 1 0 1 2 3 4 7 2 3 9 8 9 8 4 7
そのベクトルを変更したくない場合は、別のベクトルを作成するため、priority queue
を使用する必要があります。ただし、編集する余裕がある場合は、make_heap
を使用すると、補助スペースが作成されず、そのベクトルがインプレースで変更されないため、スペースが節約されるため、明らかに優れています。さらに、優先キューは簡単に実装できます。たとえば、要素のポップ中にmake_heapを使用する場合、最初にpop_heap
、次にpop_back
..の2つのコマンドを使用する必要がありますが、優先度付きキューの場合は1つのコマンドで実行できます。同様に、要素をヒープにプッシュしている間。
これで、優先キューはコンテナではなく、make_heap操作で使用されるのと同じヒープ操作を使用するベクトルまたはdequeとして基礎となるコンテナを使用するため、両方のパフォーマンスは同じになります。したがって、要件に応じて1つを使用する必要があります。