Logic behind the custom comparator for std::priority_queue - c++

I am trying to write a custom comparator for the following priority queue:
priority_queue<pair<int,int>,vector<pair<int,int>>,cmp> pq;
The first int in the pair is the key, and the second int is the count of the key. I want my priority queue to put the pair with the largest count at the top.
The following is my functor that has fulfilled my need:
class cmp{
public:
bool operator()(const pair<int,int> a, const pair<int,int> b)const{
return a.second < b.second;
}
};
I do not quite understand why this is the correct way to do it.
Why the comparator returns a.second < b.second instead of a.second > b.second, since I want to put the pair with the most count at top?
And how the priority queue will actually utilize this comparator?

From std::priority_queue's documentation:
Note that the Compare parameter is defined such that it returns true if its first argument comes before its second argument in a weak ordering. But because the priority queue outputs largest elements first, the elements that "come before" are actually output last.
The same happens with std::make_heap(): providing a comparator that returns a < b – what std::less does – results in this function template building a max-heap, i.e., the element with the highest key is at the top of the heap. If you want to build a min-heap, you have to pass operator> instead of operator< or still, use operator< but flip the operands.
To conclude, both std::priority_queue and the function templates for dealing with binary heaps sort their elements so that the top corresponds to the element with the highest key. How two keys compare to each other is, however, up to you. You can control this by providing a custom comparator.

Related

How do I interpret this template signature of priority queue?

template <class T, class Container = vector<T>, class Compare = less<typename Container::value_type> > class priority_queue;
I understand the first two template arguments, the first template argument is the data type of the element being stored in the priority queue and the second one is the kind of container that the programmer wants to use, it could be either deque or vector.
But the third argument confuses me a bit, because I have never seen something like it. I'd have done something like:
template <class T, class Container = vector<T>
class priority_queue{
/* Implementation */
};
Does it have something to do with the strict weak ordering criterion necessary for priority queue? If yes, how can I learn more about it? Could you give an example of using the third argument?
I am new to template programming, so I'd really appreciate your help.
The third parameter specifies the comparator class.
The comparator class is responsible for comparing queue elements, in order to determine the queue order. You already understand that the elements in the queue are ordered with the "higher" values first. Well, this is what defines what "higher" means, here.
The comparator class has a simple interface: given two values, return true if the first value is less than the second value, and false otherwise. The default implementation, std::less, uses the traditional < operator to compare the two values.
Use a custom comparator class in order to change the behavior of the priority queue. One example would be to specify std::greater instead of std::less as the comparator class. std::greater uses the > operator, so this creates a priority queue "in opposite order", which gives you the lowest values first, rather than highest one.
Or, you could create your own custom comparator class, such as:
class last_four_bits {
public:
bool operator()(int a, int b) const
{
return (a & 0x0F) < (b & 0x0F);
}
};
This comparator class compares the least four bits of an int only. This, in turn, makes this:
std::priority_queue<int, std::vector<int>, last_four_bits>
look at the least four bits of each int value in the queue, thus ordering all ints with the highest values in the last four bits before the ones with the lesser values, ignoring all other bits in the int.
P.S. Comparator classes are also used with associative containers, sets and maps, and serve the same function there. By carefully crafting a comparator class you can create sets and maps whose iterators iterate over the keys in the set/map in some order other than the lowest to the highest keys (as you understand "lowest" and "highest" to mean, intrinsically).

Why greater<int> and less<int> showing opposite behaviour?

Please consider following code,
using namespace std;
std::priority_queue<int,vector<int>,std::greater<int>> queue; //first
queue.push(26);
queue.push(12);
queue.push(22);
queue.push(25);
std::cout<<queue.top()<<endl;
std::priority_queue<int,vector<int>,std::less<int>> queue2; //second
queue2.push(26);
queue2.push(12);
queue2.push(22);
queue2.push(25);
std::cout<<queue2.top()<<endl;
Output:
12
26
In first definition I used greater<int> still I am getting 12 (min value) as output, while when I use less<int> I get 26 (max value).
Shouldn't greater<int> create max heap?
As far as the internal algorithm itself is concerned, std::priority_queue always creates "max heap". You just need to teach it to compare the elements in order for it know what's "max".
To determine the ordering for that "max heap", it uses a "less"-style comparison predicate: when given a pair (a, b) (in that specific order) the predicate should tell whether a is less than b. By using the ordering information obtained from the predicate std::priority_queue will make sure that the greater element is at the top of the heap. Standard std::less is an example of such predicate. Whatever predicate you supply, the implementation will treat it as a "less"-style predicate.
If you supply a predicate that implements the opposite comparison (like std::greater), you will naturally end up with minimum element at the top. Basically, one can put it this way: std::priority_queue expects a "less" comparison predicate, and by supplying a "greater" comparison predicate instead you are essentially deceiving the queue (in a well-defined fashion). The primary consequence of that deceit is that for you (the external observer) "max heap" turns into a "min heap".
And this is exactly what you observe.
Because that's their job. less and greater are supposed to model operators < and > respectively and are used by priority_queue to give order to its elements.
They yield opposite results because they're defined to do so (except for equal elements).
Shouldn't greater<int> create max heap?
You're mistaking internal representation of the container with the interface of top() member function, which is supposed to yield the top element, as per the comparator.
std::priority_queue is a "max heap". You provide a less-than operator; and the element at the top is the largest.
In your second example, you provided less-than to be the intuitive std::less; and you see the largest element at the top.
In your first example, you consider a larger int to be "less-than" a smaller int; and the "largest" element based your "less-than" is in fact the smallest int.

Priority queue to push pair and int

I want to use priority_queue like this:
priority_queue< pair< int, int> ,int ,cmp >
The comparision should be based on the int value in non-decreasing order.
Example:
((2,5),1),((2,5),2),((2,5),3)
Read the template parameters of std::priority_queue again. The second parameter is the underlying container. You can't use an int.
What you seem to be asking is how to store a pair and an int in a priority queue and sort by the int. Well, you've already figured out how to store a pair of ints. Simply expand that idea and store a pairof a pair and int. That's a naïve solution though. Instead, I recommend using a struct with the pair and the int as members so that you can give them descriptive names. Consider using a struct for the pair of ints too. Then simply use a compare functor which compares the third int only in the order of your choosing ignoring the pair.
priority_queue accepts only "one element" by saying one element I mean only a single element of arbitrary type. For having a pair, and int and another component in the priority queue you need to bring all of them as one meaning you need to be build a struct which will hold them. Then you have to use the bool operator < to tell the compiler how to compare the elements of your type .
bool operator < ( const structName& left, const structName& right)
{
return left.number < right. number;
}
this means that the comparison must be done with the member named number.

Is there an O(1) way to turn a std::map<keytype, value> into a std::set<keytype>

I'd like to do some set intersection operations on the keys of a std::map<> instance without duplicating the keys into a std::set<> beforehand.
It's not documented in the API, but is there any way in O(1) time to extract the keys from a std::map<> and put them into a std::set<>?
Create an iterator adapter that returns first for the map and use it with set_intersection.
Firstly no, there is not an O(1) operation to get a set of keys from a map. Constructing the set would have to be Omega(n).
However, you can do the set_intersection you want directly on the map and the other range, whatever that is. Untested code:
template <typename InputIterator, typename Map, typename OutputIterator>
void map_set_intersection(InputIterator first, InputIterator last, const Map &m, OutputIterator o) {
std::set_intersection(
first, last,
m.begin(), m.end(),
o,
KeyCompare<typename Map::value_type, typename std::iterator_traits<InputIterator>::value_type>()
);
}
All the magic is in the KeyCompare type:
template <typename MapValue, typename SetValue>
struct KeyCompare {
bool operator()(const MapValue &lhs, const SetValue &rhs) {
return lhs.first < rhs;
}
bool operator()(const SetValue &lhs, const MapValue &rhs) {
return lhs < rhs.first;
}
};
std::set_difference is defined to copy values from the first range specified, which in this case is the range defined by the iterators. It can do that provided that it can compare values in the first range with values in the second range in either order (which it can, thanks to KeyCompare). The two ranges don't need to have the same type, so you don't need a set of keys from the map.
If you're already using the 6-parameter form of set_intersection, then you need to change KeyCompare so that after taking the key out of the map entry, it uses your comparator instead of <.
If you're using a custom comparator or overloaded operator< and the value type of the iterator range is the same as the value type of the map, then this doesn't work. KeyCompare can no longer use the types to work out which order the arguments have been provided and hence which one it should extract the first out of. I don't think this is a very common scenario, but I also don't see an escape from it using this approach. You'd need Crazy Eddie's solution, adapt the map iterator. Which amounts to this question: Iterate keys in a C++ map
You can do an O(N) set intersection on your map. Remember that when you iterate through a map, the keys are in order, so you can use an algorithm very much like a merge operation...
All you do is maintain an iterator into each map, and increment the one whose key is smallest. If the keys are equal, you can add to a deque, list or vector (and in that case, you would increment both iterators). As soon as one of the iterators reaches the end of its map, you are done.

stl priority queue based on lower value first

I have a problem with stl priority queue.I want to have the priority queue in the increasing
order,which is decreasing by default.Is there any way to do this in priority queue.
And what is the complexity of building stl priority queue.If i use quick sort in an array which takes O(nlgn) is its complexity is similar to using priority queue???
Plz someone ans.Advanced thanx.
priority_queue has template parameters that specify the container type and the comparison to use for ordering. By default, the comparison is less<T>; to get the opposite ordering, use priority_queue<T, vector<T>, greater<T>>.
Insertion into a priority queue takes logarithmic time, so building a queue of N items has complexity O(N logN), the same as building and sorting a vector. But once it's built, insertion into the priority queue is still logarithmic, while insertion into a sorted vector is linear.
Use the type
priority_queue<T, vector<T>, greater<T> >
Use a different comparator as the 3rd template argument of std::priority_queue.
priority_queue is a container adaptor that works on any sequence you define. The performance of insertion is equal to the std::push_heap operation and takes logarithmic time. So the complexity to sorting after all insertions are done isn't equal. If you insert a fixed amount and work the queue afterwards a vector and a single sort could be more efficient.
You can just push values multiplying them by -1 so it will be stored in reverse order unlike the actual default order...and multiply the value with -1 again while retrieving data to get it in its actual form.
Replace T bellow with variable type. You will get your work done
priority_queue<T, vector<T>, greater<T> > PQ;
As example for int type variable you can write it like this:
priority_queue<int, vector<int>, greater<int> > Q;
You can use comparator class/struct to achieve the same in priority queue.
class CompareHeight
{
public:
bool operator()(int &p1, int &p2)
{
return p1 > p2;
}
};
Now declare the priority queue as
priority_queue<int, vector<int>, CompareHeight> pq;