简体   繁体   中英

Order of elements with the same priority in std::priority_queue

I have following code:

#include <iostream>
#include <map>
#include <list>
#include <queue>
#include <memory>

enum class CommandType : uint8_t {
  Comm1,
  Comm2, 
  Comm3, 
  Comm4, 
  Comm5 
};

enum class Priority {
  HIGH,
  MEDIUM, 
  LOW
};

std::map<CommandType, Priority> priorities = {
    { CommandType::Comm1, Priority::LOW }, 
    { CommandType::Comm2, Priority::LOW }, 
    { CommandType::Comm3, Priority::LOW }, 
    { CommandType::Comm4, Priority::LOW }, 
    { CommandType::Comm5, Priority::LOW }
};

class QueueEntry 
{
 public:
    QueueEntry(CommandType type)
        : type_(type) {}
    CommandType getType() { return type_; }

private:
  CommandType type_;
};

struct QueueEntryPriorityComparator  {
    bool operator()(std::unique_ptr<QueueEntry>& entry1, std::unique_ptr<QueueEntry>& entry2) const
    {
        auto p1 = priorities.at(entry1->getType());
        auto p2 = priorities.at(entry2->getType());

        return p1 < p2;
    }
};

std::priority_queue<std::unique_ptr<QueueEntry>, std::deque<std::unique_ptr<QueueEntry>>, QueueEntryPriorityComparator> q;

void print()
{
    decltype(q) queueCopy;
    queueCopy.swap(q);

    while (!queueCopy.empty()) {
        auto& top = queueCopy.top();

        std::cout << "Command type: " << static_cast<uint32_t>(top->getType()) << std::endl;;

        q.emplace(std::make_unique<QueueEntry>(top->getType()));

        queueCopy.pop();
    }
}

int main()
{


    q.emplace(std::make_unique<QueueEntry>(CommandType::Comm1));
    q.emplace(std::make_unique<QueueEntry>(CommandType::Comm2));
    q.emplace(std::make_unique<QueueEntry>(CommandType::Comm3));
    q.emplace(std::make_unique<QueueEntry>(CommandType::Comm4));
    q.emplace(std::make_unique<QueueEntry>(CommandType::Comm5));

    print();

    std::cout << std::endl;

    print();

    std::cout << std::endl;

    print();



    return 0;
}

Each element has the same priority which is Priority::LOW ;

The question is, how those elements are placed in priority_queue ?

Whenever I print the queue, elements are in different positions. It is important to me that, if any new element comes to priority_queue should be placed after the last element with the same priority.

Can it be achieved by using std::priority_queue or I must wrap it on my own?

The question is, how those elements are placed in priority_queue?

In an order that satisfies the heap property. Which for equal elements is any order.

It is importatnt to me that, if any new element comes to priority_queue should be placed after the last element with the same priority.

You can achieve that by making the order part of the priority. But to make it part of the priorty, you need to first make the order part of the element. You can use a running counter:

struct OrderedQueueEntry {
    QueueEntry entry;
    int index;
};

struct OrderedQueueEntryPriorityComparator  {
    bool operator()(std::unique_ptr<OrderedQueueEntry>& left, std::unique_ptr<OrderedQueueEntry>& right) const
    {
        auto pl = priorities.at(left->entry.getType());
        auto pr = priorities.at(right->entry.getType());

        return pl == pr
            ? left->index < right->index;
            : pl < pr;
    }
};

int i = 0;
q.emplace(std::make_unique<OrderedQueueEntry>({CommandType::Comm1, i++}));
q.emplace(std::make_unique<OrderedQueueEntry>({CommandType::Comm2, i++}));
q.emplace(std::make_unique<OrderedQueueEntry>({CommandType::Comm3, i++}));
q.emplace(std::make_unique<OrderedQueueEntry>({CommandType::Comm4, i++}));
q.emplace(std::make_unique<OrderedQueueEntry>({CommandType::Comm5, i++}));

If you want a priority queue guaranteeing that equal-comparing elements preserve insertion order, you need to write it yourself.

Certainly std::priority_queue does not guarantee this insertion ordering (and if it's implemented as a heap, that doesn't guarantee it either).

The easiest way to write something correct is probably with map<key, list<val>> , although that's probably not ideal if you need it to be fast. The suggestion to manually stable_sort a container has worse algorithmic complexity but probably performs better in most real-world situations despite that.

std::priority_queue is implemented by using std::make_heap , std::push_heap , and std::pop_heap on the underlying container. These operations are not stable so you get no guarantee that elements will stay in entered order.

If you need them to stay in entered order then one thing you can do is to write you own object that uses std::stable_sort on the underlying container when adding elements into it.

Use a std::multiset or std::multimap

Just use std::multiset or std::multimap (from your case the multimap looks like the right choice).

https://en.cppreference.com/w/cpp/container/multimap

Both is staple and does not change the insertion order (since ).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM