Why greater<int> and less<int> showing opposite behaviour? - c++

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.

Related

lower_bound function not working properly?

I am using lower_bound to search for element greater than or equal to key in the range [back,end) of a vector which may contain decreasing elements in the given range. So ,I also have to use the greater() functor. The value returned should be the last element itself(for the given range), but the function returns 0. Why?
vector<int>v={1,2,3,4,5};
int i=3;
cout<<*lower_bound(next(v.begin(),i+1),v.end(),v[i],greater<int>());
expected output : 5
if i remove the greater() code, it works fine.
I am using lower_bound to search for element greater than or equal to key in the range [back,end) of a vector which may contain decreasing elements in the given range. So ,I also have to use the greater() functor.
vector<int>v={1,2,3,4,5};
std::lower_bound requires that the input range is sorted according to the comparison object argument. Your vector is not sorted with the std::greater. If it was, then the greatest value would be first.
If your vector is sorted such that least value is first, then you must use std::less (which is the default, so does not need to be specified explicitly).
That said, next(v.begin(),3+1) is an iterator to the last element of the array, so the range [next(v.begin(),3+1), v.end()[ contains exactly one element, whose value is 5. So this range of a single element is in fact sorted according to all possible comparison functions, so using std::greater is technically not a problem.
So, std::lower_bound returns the iterator to the first value that does not satisfy the comparison (i.e. is not less than when using std::less or is not greater than when using std::greater). Otherwise it returns iterator to the end of the range, which in your case is one past the last element of the array.
You've given v[3] i.e the value 4 as the compared value. The range that contains the value 5 does not contain any elements that are not greater than 4, and therefore std::lower_bound returns the iterator to the end of the range. You indirect through this iterator, and the behaviour of the program is undefined.
If you don't use std::greater i.e. you implicitly use std::less, then the range that contains value 5 does contain an element that is not less than 4. Namely the one element whose value is 5. Thus std::lower_bound returns iterator to that element, and indirection through that iterator produces the value 5.
You're not supposed to use the reference to std::greater.
The std::lower_bound function uses a comparison operator to work out whether the element you're searching for is greater than or lower than the elements in the collection. The function presumes that the comparison operator is for less-than, i.e. std::less, and if you provide std::greater instead, the algorithm will get confused and evaluate items in reverse order.

What is the meaning of the comparator part of the std::priority_queue in C++?

Priority queue syntax in C++:
priority_queue <Type, vector<Type>, ComparisonType > min_heap;
If I want to declare a max heap, I use std::less, otherwise std::greater (min heap). I don't quite get why std::less leads to a max heap, and std::greater min heap?
A quote from cppreference that explains the concept:
Compare - A Compare type providing a strict weak ordering.
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. That
is, the front of the queue contains the "last" element according to
the weak ordering imposed by Compare.

How does sorting in descending order works in c++?

I was searching how to sort a vector in a descending order then I found this code
std::sort(numbers.begin(), numbers.end(), std::greater<int>());
It works but I just want to know how it works
The third argument to std::sort is a functor/function that returns true if the first argument is to be placed before the second element in the sort order.
std::greater<int>::operator()(...) returns true if the first argument is greater than the second argument.
Consequently, use of std::greater<int>() as the third argument to std::sort results in the collection of objects to be sorted in descending order.
The std::sort function expects you† to give it a function that tells it how to sort a range in ascending order, so by default it'll use something like std::less<int>().
By providing a function with the opposite effect (i.e. std::greater<int>()) you are turning that expectation literally on its head, resulting in a completely legal "mirror-image" of the default behaviour. That is, a reverse sort.
A different way of explaining it is that, at each step of its algorithm, when it wants to know whether one element A should go "before" another element B, it will ask std::greater<int>(), which will tell it: yes, one element A should go "before" another element B if A > B, and there you go.
5 3 2 1
> > >
This is contrary to the default behaviour which uses something like < instead:
1 2 3 5
< < <
† Of course, you can actually give it a function that defines any other order you like (subject to certain constraints), so there are plenty more options. Here I explore only the two most obvious sorting methodologies.
The documentation for std::sort (here amongst other places) says things like
Sorts the elements in the range [first, last) in ascending order.
This is a convenient simplification, but it's confusing when you don't want ascending order.
It's more useful to think that sort rearranges your range according so some function - let's call it pre - which says for any pair of elements, which one should come before (precede) the other.
That is, the function looks something like
bool pre(int a, int b); // return true if a should precede b
The default behaviour, if you don't specify something else, is to use std::less, which will do roughly this:
bool less(int a, int b) { return a<b; }
and this default (where the smaller value always precedes the larger) gives you a result sorted in ascending order.
If you specify std::greater, you're saying the larger value should always precede the smaller (because if greater(a,b)==true, that means both that a>b and also that a will precede b in the output).
So, this is the same as saying the result is sorted in descending order.
NB. I called the comparison pre both because it tells you which elements should precede which, and also because it is a predicate (specifically a BinaryPredicate and even more specifically a Comparison).

What is the behavior of std::greater in std::priority_queue?

When I used std::greater<int> as a comparison function object in std::sort as the following:
std::sort(array_name, array_name + length, std::greater<int>());
The array sorted in non-increasing order (i.e. the largest number first)
But, when I used the same comparison function in std::priority_queue as the following:
std::priority_queue <int, std::vector<int>, std::greater<int> > pq_name;
Why the std::priority_queue::top member function returns the smallest number first?
(I need a clear comparison between the two cases).
In order to understand why this happens, you have to understand the data structures that you're working with.
Q: What is an array?
A: An array is a contiguous region of memory containing a type. As far as we are concerned it can contain any type of object. But since you're working with int, in C++ an integer is tipicaly formed by 32 bits, but it depends. Therefore, your representation in memory would be something like,
| 4 bytes | 4 bytes | 4 bytes | (...)
When you're sorting an array, the std::greater<int>operator will "sort" your array by keeping the greater integer in the first position, the second largest value in the second position, and so on.
Q: What is a std::priority_queue?
A: A priority queue is a heap, more specifically, a binary tree. A heap is a data structure that is optimized to return the largest value by default. This type of heaps are called max heaps. But when you overload the comparison within the priority_queue using the std::greater operator, you are transforming the heap into a min heap. This kind of heaps, similarly to what happens when you overload the std::sort method operator, is implemented to retrieve the minimum value first.
Keep in mind that the representation of a heap in memory is completely different from the one found in an array.
CPPRef says on it:
A user-provided Compare can be supplied to change the ordering, e.g. using std::greater would cause the smallest element to appear as the top().
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. That is, the front of the queue contains the "last" element according to the weak ordering imposed by Compare.
So, for a default std::less-based ordering, top of the queue is the largest element, the one that comes the last when sorted. OTOH with std::greater the last element is the smallest one.

How does the following comparator even works while building up the min heap?

I know that if I build a heap using STL, it makes a max_heap. And if I want to make a min_heap, I will have to write my own custom comparator. Now, the following comparator,
struct greater1{
bool operator()(const long& a,const long& b) const{
return a>b;
}
};
int main() {
std::vector<long> humble;
humble.push_back(15);
humble.push_back(15);
humble.push_back(9);
humble.push_back(25);
std::make_heap(humble.begin(), humble.end(), greater1());
while (humble.size()) {
std::pop_heap(humble.begin(),humble.end(),greater1());
long min = humble.back();
humble.pop_back();
std::cout << min << std::endl;
}
return 0;
}
The above code I got from off of the internet. I just have one doubt. How is the comparator actually working. And as far as I understand, shouldn't it be something like, return a < b because we want the minimum element to be in the front and then the bigger element in the heap. Why is it return a > b. Doesn't it mean that, if (a>b), then this will return true and a will be put in the heap before b and therefore a bigger element is put in front of a smaller element?
I think you're reading too much into a connection between the comparator semantics and the heap semantics. Remember, the internal details and structure of containers are deliberately abstracted away from you so, the moment you started trying to rationalise about this in terms of how you think the internal structure of a max_heap should look, you got carried away.
In the standard library, default comparators are always less-than. If the relationship between elements for sorting within the particular container/algorithm is not less-than, the container/algorithm will be clever enough to make that transformation (in this case, on usual implementations, by simply passing the operands in the reverse order, like cmp(b,a)!). But, fundamentally, it will always start with a less-than ordering because that is the consistent convention adopted.
So, to invert the ordering of your container, you would take a less-than comparator and turn it into a greater-than comparator, no matter what the physical layout of the container's implementation may (in your opinion) turn out to be.
Furthermore, as an aside, and to echo the Croissant's comments, I would take longs by value … and, in fact, just use std::greater rather than recreating it.
Standard heap builds in such a way that for every element a and its child b, the comparison cmp(b,a) holds, where cmp is the comparator supplied. Note that the first argument to cmp is the child. (Or, abstracting from the internal representation, the standard way is so that cmp(top, other) is true for the first element top and any other other.)
This is obviously done to make the default comparator ("less") build max-heap.
So you need to provide a comparator which you want to return true when a child is supplied as a first argument. For the min-heap, this will be 'greater'.