Як використовувати чергу пріоритетів 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>який може діяти у двох Personвипадках.


7
Хоча ця відповідь правильна, мені не подобається використовувати operator<тут. operator<реалізує порівняння за замовчуванням для типу, яке, на мій досвід, рідко буває тим, що ви хочете. Я думаю, що підхід, який Майк описує у своїй відповіді, майже завжди кращий.
Björn Pollex

1
@ BjörnPollex Погоджено. Я щось про це додавав. У класі з одним членом даних оператор міг би мати сенс.
juanchopanza

Достойно відзначити наступне : впровадження bool YourClass::operator <(const YourClass&) constдозволить також прозоре використання компаратора за замовчуванням std::less<T>. Не такий гнучкий, але функціональний, коли це все, що вам потрібно. (і +1).
WhozCraig

Дякую за відповідь. Я можу перевантажити оператор '<', навіть якщо клас має кілька членів, так?
user2441151

@ user2441151 Так, можна, але треба бути обережним з логікою. Він повинен виконувати суворе слабке впорядкування. Чим більше членів даних, тим легше помилитися. Це хіба що ви використовуєте std::tie, і в цьому випадку це досить тривіально.
juanchopanza

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

1
@castarco так, ви можете передати конкретний об'єкт порівняння як аргумент конструктора.
Mike Seymour

Це більш кращий метод, ніж найкраща відповідь. Не лише завдяки тому, що воно досягає порівняння вищого рівня (яке можна легко передати іншими мовами, нижчого та вищого рівня), але й тому, що це призводить до більшого використання коду.
Фуркан Топрак

20

Черга пріоритетів - це абстрактний тип даних, який фіксує ідею контейнера, до елементів якого прикріплені "пріоритети". Елемент найвищого пріоритету завжди відображається в передній частині черги. Якщо цей елемент видалено, наступний елемент з найвищим пріоритетом рухається вперед.

Стандартна бібліотека C ++ визначає шаблон класу priority_queue з наступними операціями:

push : Вставте елемент у чергу пріоритетів.

top : Повернути (не видаляючи) елемент із найвищим пріоритетом із черги пріоритетів.

pop : Вилучіть елемент з найвищим пріоритетом із черги пріоритетів.

size : повертає кількість елементів у черзі пріоритетів.

empty : Повертає true або false залежно від того, порожня черга пріоритетів чи ні.

Наступний фрагмент коду показує, як створити дві черги пріоритетів, одну, яка може містити цілі числа, а іншу, яка може містити рядки символів:

#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 має три поля: години, хвилини, секунди:

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
У мене є запитання, якщо можна. приоритетна_черга <Час, вектор <Час>, Порівняти Час> pq; . Чому необхідний другий параметр, вектор <Time>? Я бачив це в численних фрагментах коду, але я не міг цього зрозуміти.
BarbuDorel

Ой ні ... лисиця не бура, а не коричнева лисиця стрибнула (перестрибнула) через ледачого собаку. :-(
cyber_raj

3
Чи не повинен pq.front () бути pq.top () у першому фрагменті?
кодування

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.