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.
Related
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.
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).
My program is based around a set of pairs, namely
typedef std::pair<int,int> innerPair;
typedef std::pair<innerPair,int> setElement;
std::set<setElement> Foo;
The innerPair element is what really defines the setElement, but I need to attach a group ID to every element, hence the latter setElement definition.
In the remainder of the program, I need to find innerPair regardless of their group ID so I basically need a function
std::set<setElement>::iterator find(innePair);
that will find the innerPair regardless of its group ID. As it stands I could simply cycle through all available group ID and do multiple find() calls, but it is far from efficient.
Is there a concise way of defining a find( ... ) member function that perform some sort of wildcarded search, or do I need to overload it with my own definition?
If you have multiple elements with the same inner pair and a differing group id, you could use std::multimap<innerPair, int>.
This allows you to store multiple elements with the same innerPair.
It also simplifies searching with lower_bound/upper_bound or equal_range.
I see two possible designs for this. One may be more applicable than the other.
It may be more appropriate for innerPair to be a struct with 3 members (first, second, and group ID) or an std::tuple<int, int, int>. Is the group ID part of the innerPair objects? If so, then I suggest this is the better design.
If not (and to be honest, I think in your situation it's not), you should be using an std::map<innerPair,int> to create a mapping from innerPair objects to group IDs. You can then find an element easily with:
std::map<innerPair,int> mapping;
// Fill your map
innerPair key = {1, 2};
auto found_iter = mapping.find(key);
You can also get the group ID for a specific innerPair with:
int group_id = mapping[key];
You don't need to provide a custom comparator because operator< is already defined for std::pair .
If you want to search by a partial object, you are probably best off not to use a std::set<...> but rather a std::map<...>:
std::map<std::pair<int, int>, int>
This has nearly the value type you targeted for your std::set<...> anyway (the difference is that the first member is declared to be const).
If you really insist in using a std::set<...> you'd needto create a comparator type which ignores the last second member. I'm not sure if std::set<...> supports mixed type comparisons (I think it does not).
You could use a multiset, with a custom comparison function, or functor for instance:
struct innerPairCompare
{
bool operator () (const setElement &a, const setElement &b)
{
const innerPair &a_ = a.first;
const innerPair &b_ = b.first;
return (a_.first > b_.first || a_.first = b_.first && a_.second > b_.second);
}
};
then use it for your multiset:
std::multiset<setElement,innerPairCompare> Foo;
It will store all the setElements with the same innerPair in the same list.
Alternatively, if you need all the innerPair with a given GroupID, use a function comparing on groupID.
Finally, if you don't really need to keep the GroupID and the innerPair together, you could use a map (or multimap), using the GroupID or the innerPair as the Key.
If you want to keep using an std::set then you can use std::find_if with a custom predicate, take a look at this answer.
Basically you will define a function
bool pairCorresponds(std::pair<int,int> element)
that will do the work for you.
I currently have an array of pair<double, int> which I sort using a simple custom comparator function e.g.
// compare by first
int sort_index_lcomparator(const pair<double, int>& a, const pair<double, int>& b) {
return a.first < b.first;
}
// then sort simply like
pair<double, int> arr[size];
std::sort(arr, arr + size, sort_index_lcomparator);
I'm actually interested in the index order and not in the sorted doubles. My problem is that I would like to change away from this structure and have instead a struct of two arrays rather than an array of a struct i.e. I would like to optimize for locality and auto-vectorization but in this case I need an overloaded swap which is attached to a type specifically. I guess I would need something along the lines of redefining swap for the double type and keep both arrays in sync in such custom swap. Is there a way to "override" swap in such a manner within a limited scope?
I have one proposal for you: make the index array the one you sort and keep the values as global array. From then on: sort based on comparator that accepts indices, but actually compares based on the values.
You should specialize std::sort using your custom "comparator".
template <class RandomAccessIterator, class Compare>
void sort ( RandomAccessIterator first, RandomAccessIterator last, Compare comp );
By default sort uses a standard comparator which just compares the elements referenced by the given iterators.
Using the custom Compare you may override this. Note that it's not a function (in C++ generally you may not pass a function as a template parameter). It's a class. You pass an object of this class to sort, whereas this object should implement the following operator:
bool operator () (const Type1 &a, const Type2 &b);
So, you may invoke sort for array of your double's. Your comparator should have pointers to the beginning of both your arrays: double and int.
In case with arrays the iterator resolves into a pointer to an array element. Using the array starting address you may convert it into an index, and use it to access the second array.
I have encountered a problem that I want to define a map, that is sorted internally by the first's descending order. if the first is not a primary type, like if it's a class, I can just overload the "<" within that class, but I don't know how to deal with the int type. Any suggestion?
Thanks a lot!!
Add a comparator:
#include <functional>
map<int, value_type, greater<int>> m;
The default is less<int>.
You can specify a comparator when you create the map (it's an optional constructor argument).
e.g.:
bool my_compare(int x, int y) { return y < x; }
std::map<int,T,bool(*)(int,int)> my_map(my_compare);
Notice that I've needed to explicitly specify a 3rd template parameter as well.
[Note: I would strongly advise that you don't overload the < operator to perform >...]
Look at the std::less implementation: http://www.cplusplus.com/reference/std/functional/less/
You may write own comparator and use it together with map.