The reason of using `std::greater` for creating min heap via `priority_queue`
The logical argument is as follows
std::priority_queue
is a container adaptor; basic memory considerations make the back the preferred place for modifications (withpop_back()
andpush_back()
) for sequence containers such asstd::vector
.- the
priority_queue
primitives are based onstd::make_heap
(constructor),std::pop_heap
+container::pop_back
(priority_queue::pop
) and oncontainer::push_back
+std::push_heap
(priority_queue::push
) pop_heap
will take the front of the underlying storage, and put it at the back, restoring the heap invariant afterwards. The reverse goes forpush_heap
.- doing
sort_heap
on amax_heap
(with the max at the front initially) will repeatedly pop the front to the back and sort the range according toless
(which is the default comparison operator) - hence, the preferred implementation of a
max_heap
is to have the max element w.r.t.less
at the front, accessed throughpriority_queue::top
(underlyingcontainer::front
). - one can still debate whether it is intuitive that
priority_queue
with astd::less
comparator is representing amax_heap
. It could have been defined as amin_heap
by reversing the comparator's arguments (but see the comment by @T.C. that with C++98 binders this is rather verbose) everywhere in the calls to the various heap functions. The one (for me) counter-intuitive result would have been thattop()
would then not have given the element with top priority
Min heap with std::priority_queue is my bottleneck
http://demangler.com translated that function into: (indented by me)
RKD<Division_Euclidean_space<float> >::nn(
unsigned int,
std::vector<float, std::allocator<float> > const&,
float const&,
std::vector<std::pair<float, int>, std::allocator<std::pair<float, int> > >&,
int&,
int,
unsigned int*,
std::priority_queue<std::tuple<float, int, int>,
std::vector<std::tuple<float, int, int>,
std::allocator<std::tuple<float, int, int> > >,
std::greater<std::tuple<float, int, int> > >&,
float const&,
unsigned int const&,
std::vector<float, std::allocator<float> > const&,
std::vector<float, std::allocator<float> > const&,
int)
I don't know what nn
does, but I suppose it does a lot more than priority queue operations.
std::priority_queue and make_heap API design
I find it confusing too, but from a somewhat different perspective. Suppose that you want to sort a sequence in ascending order. There are several ways of doing it:
- Put your data in a
std::vector
orstd::deque
and runstd::sort()
usingstd::less
. - Put your data in a
std::list
and runstd::list::sort()
usingstd::less
. - Insert your data in a
std::set
that is configured withstd::less
and in the end it is automatically sorted. - Put your data in a
std::vector
orstd::deque
and runstd::make_heap()
followed bystd::pop_heap()
-s usingstd::less
. - Push your data through a
std::priority_queue
usingstd::greater
(!!!).
As we can see, std::priority_queue
is a definite outlier from such a point of view.
Actually, the reason behind the confusing behavior of std::priority_queue
in this regard is hidden in item (4) since that is what a std::priority_queue
does underneath. (4) is also against my intuition (though to a lesser extent), since in the intermediate state (while not all std::pop_heap
have been performed) the sorted part of the sequence is in its upper range, rather than the lower range.
But it also explains why a max heap was selected for the standard library - std::pop_heap
puts the popped element in the location, from where it can be removed in constant time, regardless of the container type used.
Type error in C++ when instantiating a priority_queue of int pairs with a custom comparator (to implement a min heap)
As you can see in the std::priority_queue
documentation:
- The 1st template argument is the data type (should be
pair<int,int>
in your case). - The 2nd template argument should be the underlying container type used for the queue.
- The 3rd should be the compare type.
For example if you want a priority_queue
of int
pair
s that use std::vector
as a container, you need:
std::priority_queue<std::pair<int, int>, // data type
std::vector<std::pair<int,int>>, // container type
std::greater<std::pair<int,int>>> pq; // compare type
Note: as you can see here the std::pair
implementation for comparison operators which are used by std::greater
is:
Compares lhs and rhs lexicographically by operator<, that is, compares
the first elements and only if they are equivalent, compares the
second elements.
If this is not what you need you can implement your own compare type for std::pair<int,int>
.
A side note: it is better to avoid using namespace std
. See here: Why is "using namespace std;" considered bad practice?.
How to make a min heap of tuples sorted by the 2nd element?
You can do this in two ways:
Change the order of the tuple so the column you want to sort is the first in the tuple.
Create a custom comparator outside of your function definition.
Struct Comparator {
bool operator()(tuple<int, int>& t1, tuple<int, int>& t2) {
return std::get<1>(t1) > std::get<1>(t2);
}
}
Then, you can use this comparator instead of greater<tuple<int, int>> as such:
priority_queue<tuple<int, int>, vector<tuple<int, int>>, Comparator> pq;
This comes in very handy when you're dealing with complex objects and you want to sort by a specific attribute.
Why does priority_queue in STL not follow strict weak ordering?
The problem is that the negation of your strict_weak_order
(that uses >
) is <=
and that is not a strict weak order. A strict weak order R
has to satisfy x R x == false
for all x
. However, R
equal to <=
yields (x <= x) == true
.
You need to reverse the order of arguments (which corresponds to <
) instead.
bool comparer_function(const int& val1 , const int& val2){
return strict_weak_order_function(val2 , val1);
}
struct Compaper_functor{
bool operator()(const int& val1 , const int& val2){
return strict_weak_order_function(val2 , val1);
}
};
Note however that a std::priority_queue
has a std::less
as default comparator, but that gives a max-heap (i.e. [5, 4, 3, 2, 1]
output from the same input), so to get a min-heap (i.e. with output [1, 2, 3, 4, 5]
from input [5, 4, 3, 2, 1]
) you need to pass std::greater
, see e.g. this:
#include <queue>
#include <iostream>
int main()
{
auto const v = std::vector<int> { 5, 4, 3, 2, 1 };
// prints 5 through 1
for (auto p = std::priority_queue<int> { v.begin(), v.end() }; !p.empty(); p.pop())
std::cout << p.top() << ',';
std::cout << '\n';
// prints 1 through 5
for (auto p = std::priority_queue<int, std::vector<int>, std::greater<int>> { v.begin(), v.end() }; !p.empty(); p.pop())
std::cout << p.top() << ',';
std::cout << '\n';
}
Live Example
Related Topics
Does Boost Have a Datatype for Set Operations That Is Simpler Than the Stl
How to Call a Function by Its Name (Std::String) in C++
Tools to Find Included Headers Which Are Unused
How to Sort a Std::Map First by Value, Then by Key
Visual Studio 2015 Doesn't Have Cl.Exe
The Copy Constructor and Assignment Operator
Are Static Variables in a Base Class Shared by All Derived Classes
A C++ Implementation That Detects Undefined Behavior
Fast Cross-Platform C/C++ Image Processing Libraries
Narrowing Conversions in C++0X. Is It Just Me, or Does This Sound Like a Breaking Change
Are There Practical Uses for Dynamic-Casting to Void Pointer
Setupdigetdeviceproperty Usage Example
Mingw .Exe Requires a Few Gcc Dll's Regardless of the Code
Forward Declaration with Unique_Ptr