简体   繁体   中英

Why is Vector used as a second argument to Priority Queue?

Why is it that when priority_queue is used with a single data type, like 'int', we initialise it like this: priority_queue<int> ; but, when it's initialized with a pair we add a second parameter of type vector priority_queue<pair<int,int>, vector<pair<int,int>>> ?

Also, I've noticed several ways to add a third argument that specifies ordering.

Method 1 - Struct

struct myCompare {
   bool operator()(const pair<int, int>& A, const pair<int, int>& B) {
       return A.second < B.second;
   }
};

priority_queue<pair<int, int>, vector<pair<int, int>>, myCompare> leaderBoard;

Method 2 - Lambda

auto myComp = [](const pair<int, int>& A, const pair<int, int>& B) 
              {return A.second < B.second;};

priority_queue<pair<int, int>, vector<pair<int, int>>, decltype(myComp)> leaderBoard(myComp);

My Questions

  1. Why is the second parameter of priority_queue a vector ? What does this mean and when does this second parameter need to be specified?
  2. In method 2, why is decltype needed with this lambda?
  3. In method 2, why does the object leaderBoard need to be initialised with (myComp)
  4. In method 2, why can I not specify my lambda as the third argument directly?
  5. What is the difference between A.second > B.second and A.second < B.second ? How do you remember which one means ascending order, and which one is descending order?

Why is it that when priority_queue is used with a single data type, like 'int', we initialise it like this: priority_queue<int> [...]?

Because std::priority_queue is a class template defined as follows:

template<
    class T,
    class Container = std::vector<T>,
    class Compare = std::less<typename Container::value_type>
> class priority_queue;

As you can see, you can instantiate this class only providing the T , because the rest of the types will be defaulted. You can read more about template default arguments here .

but, when it's initialized with a pair we add a second parameter of type vector priority_queue<pair<int,int>, vector<pair<int,int>>> ?

Because someone wanted to be explicit. priority_queue<pair<int, int>> is equivalent to priority_queue<pair<int,int>, vector<pair<int,int>>> , because the second template type ( vector<pair<int, int>> ) will be present by default.

  1. Why is the second parameter of priority_queue a vector? What does this mean and when does this second parameter need to be specified?

We don't need to specify it explicitly . The second template parameter is a type use for internal representation of data. std::priority_queue is a container adaptor , which means that it's not a container by itself - it uses some other container and wraps it with certain kind of utilities.

  1. In method 2, why is decltype needed with this lambda?

Because you need to provide a type . myCompare is a struct , so it's a name of a type. myComp is not a type, it's a variable. You wish to get its declared type ? Use decltype .

  1. In method 2, why does the object leaderBoard need to be initialised with (myComp) ?

Because you cannot default construct an object given a decltype of a lambda ( this got relaxed in C++20 ). You need to use the following constructor:

explicit priority_queue(const Compare& compare)
    : priority_queue(compare, Container()) { }

which expects a callable (in this case - the lambda) that will be used as a comparator.

  1. In method 2, why can I not specify my lambda as the third argument directly?

You mean as the third template argument? Because as of right now, lambdas cannot be used in unevaluated contexts, and providing a type for a template is one of those.

5.1. What is the difference between A.second > B.second and A.second < B.second ?

The difference is quite blatant. One checks for A.second being greater than the second argument and the other one is the reverse. It is commonly used for sorting (for comparing elements).

5.2 How do you remember which one means ascending order, and which one is descending order?

It's pretty easy - the C++ conception is to use < between the left hand side argument and the right hand side argument, like so: left_hand_side < right_hand_side .

Why is the second parameter of priority_queue a vector?

You can use any container that satisfies certain requirements . vector is a reasonable default choice.

What does this mean and when does this second parameter need to be specified?

If either you want to change its default value, or you want to specify the third one. std::priority_queue<std::pair<int,int>> is a valid type without specifying the second paramater.

In method 2, why is decltype needed with this lambda?

If you want to use a custom comparator, you have to specify its type as a third template parameter. Each lambda has its own unique type, the only way to obtain it, is through decltype .

In method 2, why does the object leaderBoard need to be initialised with (myComp)

Because struct -based comparator can be default constructed, but lambda-based one can't. The relevant constructors are

priority_queue() : priority_queue(Compare(), Container()) { }
priority_queue(const Compare& compare) : priority_queue(compare, Container()) { }

If you don't provide compare , Compare() is used. myCompare() is a valid value, and decltype(myComp)() is not.

In method 2, why can I not specify my lambda as the third argument directly?

You need a type, not a value of that type.

What is the difference between A.second > B.second and A.second < B.second?

Swapping the order of arguments (or, equivalently, < and > ) turns a min-heap into a max-heap and vice versa, ie that order determines which element will be returned by .top() , the smallest or the largest.

In method 2, why does the object leaderBoard need to be initialised with (myComp)

it depends on what C++ standard you use. Before C++20 you need to pass functor created by lambda expression to priority_queue constructor because lambdas until C++20 were not default constructible.

Since C++20 you can write:

priority_queue<pair<int, int>, vector<pair<int, int>>, decltype(myComp)> leaderBoard;

it will work fine because compiler knows type of lambda - specified by decltype(myComp) , and it can call its default ctor.


In method 2, why can I not specify my lambda as the third argument directly?

Yes, you can but you need to still use decltype to get comparator type:

priority_queue<pair<int, int>, vector<pair<int, int>>, 
     decltype( [](const pair<int, int>& A, const pair<int, int>& B) 
              {return A.second < B.second;} ) > leaderBoard;

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