オブジェクトに優先キューSTLを使用するにはどうすればよいですか?


80
class Person
{
public:
    int age;
};

Personクラスのオブジェクトを優先キューに保存したい。

priority_queue< Person, vector<Person>, ??? >

比較のためにクラスを定義する必要があると思いますが、よくわかりません。

また、私たちが書くとき、

priority_queue< int, vector<int>, greater<int> > 

大きい方はどのように機能しますか?


回答:


110

Personこの場合、キューに格納されているタイプに対して、有効で厳密な弱順序の比較を提供する必要があります。デフォルトでは、を使用しますstd::less<T>。これは、と同等の値に解決されoperator<ます。これは、それ自体が格納されているタイプに依存しています。したがって、実装する場合

bool operator<(const Person& lhs, const Person& rhs); 

それ以上の変更なしで動作するはずです。実装は次のようになります

bool operator<(const Person& lhs, const Person& rhs)
{
  return lhs.age < rhs.age;
}

タイプに自然な「より小さい」比較がない場合は、デフォルトの代わりに独自の述語を提供する方が理にかなっていますstd::less<Person>。例えば、

struct LessThanByAge
{
  bool operator()(const Person& lhs, const Person& rhs) const
  {
    return lhs.age < rhs.age;
  }
};

次に、次のようにキューをインスタンス化します。

std::priority_queue<Person, std::vector<Person>, LessThanByAge> pq;

std::greater<Person>コンパレータとしての使用に関しては、これはoperator>、デフォルトの場合と同等の機能を使用し、優先度が反転したWRTでキューを作成する効果があります。operator>2つのPersonインスタンスで動作できるの存在が必要になります。


7
この答えは正しいですが、私はoperator<ここの使用が嫌いです。operator<タイプのデフォルトの比較を実装しますが、私の経験では、これが必要になることはめったにありません。マイクが彼の答えで説明しているアプローチは、ほとんどの場合好ましいと思います。
ビョルンポレックス2013年

1
@BjörnPollex同意しました。私はそれについて何かを追加していました。データメンバーが1つしかないクラスでは、演算子理にかなっている可能性あります。
juanchopanza 2013年

注意する価値bool YourClass::operator <(const YourClass&) constがあります:実装すると、デフォルトのコンパレータの透過的な使用も可能になりますstd::less<T>。柔軟性はありませんが、必要なのはそれだけで機能します。(および+1)。
whozCraig 2013年

答えてくれてありがとう。クラスに複数のメンバーがある場合でも、「<」演算子をオーバーロードできますよね?
user2441151 2013年

@ user2441151はい、できますが、ロジックに注意する必要があります。厳密な弱順序を実装する必要があります。データメンバーが多いほど、間違いを犯しやすくなります。を使用しない限りstd::tie、それは非常に簡単です。
juanchopanza 2013年

50

次に、コンパレータクラスを記述します。

struct CompareAge {
    bool operator()(Person const & p1, Person const & p2) {
        // return "true" if "p1" is ordered before "p2", for example:
        return p1.age < p2.age;
    }
};

そしてそれをコンパレータ引数として使用します。

priority_queue<Person, vector<Person>, CompareAge>

を使用greaterすると、デフォルトlessとは逆の順序になります。つまり、キューは最大値ではなく最小値を提供します。


1
コンパレータクラスの代わりに「コンパレータオブジェクト」を渡すことは可能ですか?(パラメータ化して柔軟性を高めるため)
castarco 2014年

1
@castarcoはい、特定のコンパレータオブジェクトをコンストラクタ引数として渡すことができます。
マイクシーモア

これは、トップアンサーよりも好ましい方法です。より高いレベルの比較(他の言語、より低いレベルおよびより高いレベルで簡単に転送できる)を実現するだけでなく、より再利用可能なコードが得られるためです。
FurkanToprak20年

20

優先度付きキューは、要素に「優先度」が付加されているコンテナの概念をキャプチャする抽象データ型です。優先度が最も高い要素は、常にキューの先頭に表示されます。その要素が削除されると、次に優先度の高い要素が先頭に進みます。

C ++標準ライブラリは、次の操作でクラステンプレートpriority_queueを定義します。

push:要素を優先キューに挿入します。

top:優先度キューから最も優先度の高い要素を(削除せずに)返します。

pop:優先度キューから最も優先度の高い要素を削除します。

size:優先キュー内の要素の数を返します。

empty:優先キューが空かどうかに応じてtrueまたはfalseを返します。

次のコードスニペットは、2つの優先度キューを作成する方法を示しています。1つは整数を含むことができ、もう1つは文字列を含むことができます。

#include <queue>

priority_queue<int> q1;
priority_queue<string> q2;

以下は、優先キューの使用例です。

#include <string>
#include <queue>
#include <iostream>

using namespace std;  // This is to make available the names of things defined in the standard library.

int main()
{
    piority_queue<string> pq; // Creates a priority queue pq to store strings, and initializes the queue to be empty.

    pq.push("the quick");
    pq.push("fox");
    pq.push("jumped over");
    pq.push("the lazy dog");

    // The strings are ordered inside the priority queue in lexicographic (dictionary) order:
    // "fox", "jumped over", "the lazy dog", "the quick"
    //  The lowest priority string is "fox", and the highest priority string is "the quick"

    while (!pq.empty()) {
       cout << pq.top() << endl;  // Print highest priority string
       pq.pop();                    // Remmove highest priority string
    }

    return 0;
}

このプログラムの出力は次のとおりです。

the quick
the lazy dog
jumped over
fox

キューは優先度の規律に従うため、文字列は優先度の高いものから低いものへと出力されます。

ユーザー定義オブジェクトを含めるために優先キューを作成する必要がある場合があります。この場合、優先度キューは、どのオブジェクトが最も優先度が高いかを判断するために使用される比較基準を知る必要があります。これは、演算子()をオーバーロードするクラスに属する関数オブジェクトを使用して行われます。オーバーロードされた()は、優先順位を決定する目的で<として機能します。たとえば、Timeオブジェクトを格納するための優先キューを作成するとします。Timeオブジェクトには、時間、分、秒の3つのフィールドがあります。

struct Time {
    int h; 
    int m; 
    int s;
};

class CompareTime {
    public:
    bool operator()(Time& t1, Time& t2) // Returns true if t1 is earlier than t2
    {
       if (t1.h < t2.h) return true;
       if (t1.h == t2.h && t1.m < t2.m) return true;
       if (t1.h == t2.h && t1.m == t2.m && t1.s < t2.s) return true;
       return false;
    }
}

上記の比較基準に従って時間を保存する優先キューは、次のように定義されます。

priority_queue<Time, vector<Time>, CompareTime> pq;

Here is a complete program:

#include <iostream>
#include <queue>
#include <iomanip>

using namespace std;

struct Time {
    int h; // >= 0
    int m; // 0-59
    int s; // 0-59
};

class CompareTime {
public:
    bool operator()(Time& t1, Time& t2)
    {
       if (t1.h < t2.h) return true;
       if (t1.h == t2.h && t1.m < t2.m) return true;
       if (t1.h == t2.h && t1.m == t2.m && t1.s < t2.s) return true;
       return false;
    }
};

int main()
{
    priority_queue<Time, vector<Time>, CompareTime> pq;

    // Array of 4 time objects:

    Time t[4] = { {3, 2, 40}, {3, 2, 26}, {5, 16, 13}, {5, 14, 20}};

    for (int i = 0; i < 4; ++i)
       pq.push(t[i]);

    while (! pq.empty()) {
       Time t2 = pq.top();
       cout << setw(3) << t2.h << " " << setw(3) << t2.m << " " <<
       setw(3) << t2.s << endl;
       pq.pop();
    }

    return 0;
}

プログラムは、最新から最古までの時間を出力します。

5  16  13
5  14  20
3   2  40
3   2  26

最も早い時間を最高の優先順位にしたい場合は、次のようにCompareTimeを再定義します。

class CompareTime {
public:
    bool operator()(Time& t1, Time& t2) // t2 has highest prio than t1 if t2 is earlier than t1
    {
       if (t2.h < t1.h) return true;
       if (t2.h == t1.h && t2.m < t1.m) return true;
       if (t2.h == t1.h && t2.m == t1.m && t2.s < t1.s) return true;
       return false;
    }
};

1
よろしければ質問があります。priority_queue <Time、vector <Time>、CompareTime> pq; 。2番目のパラメーターvector <Time>が必要なのはなぜですか?多数のコードスニペットでそれを見てきましたが、理解できませんでした。
BarbuDorel 2016

ああ、いや...キツネは茶色ではなく、茶色でないキツネは怠惰な犬を飛び越えた(ジャンプした)。:-(
cyber_raj 2016

3
最初のスニペットでは、pq.front()をpq.top()にするべきではありませんか?
コードウィング2017

4

このコードが役立つかもしれません。

#include <bits/stdc++.h>
using namespace std;    

class node{
public:
    int age;
    string name;
    node(int a, string b){
        age = a;
        name = b;
    }
};

bool operator<(const node& a, const node& b) {

    node temp1=a,temp2=b;
    if(a.age != b.age)
        return a.age > b.age;
    else{
        return temp1.name.append(temp2.name) > temp2.name.append(temp1.name);
    }
}

int main(){
    priority_queue<node> pq;
    node b(23,"prashantandsoon..");
    node a(22,"prashant");
    node c(22,"prashantonly");
    pq.push(b);
    pq.push(a);
    pq.push(c);

    int size = pq.size();
    for (int i = 0; i < size; ++i)
    {
        cout<<pq.top().age<<" "<<pq.top().name<<"\n";
        pq.pop();
    }
}

出力:

22 prashantonly
22 prashant
23 prashantandsoon..

0

ユーザー定義のコンパレータを定義できます。以下のコードが役立ちます。

コードスニペット :

#include<bits/stdc++.h>
using namespace std;

struct man
{
  string name;
  int priority; 
};

class comparator
{
 public:
   bool operator()(const man& a, const man& b)
   {
        return a.priority<b.priority;
   }
};

int main()
{
   man arr[5];
   priority_queue<man, vector<man>, comparator> pq;

   for(int i=0; i<3; i++)
   {
     cin>>arr[i].name>>arr[i].priority;
     pq.push(arr[i]);
   }

   while (!pq.empty())
   {
     cout<<pq.top().name<<" "<<pq.top().priority;
     pq.pop();
     cout<<endl;
   }
   return 0;
}

入力:

バットマン2
悟空9
マリオ4

出力

悟空9
マリオ4
バットマン2

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.