Sorting a vector that contains pair<x,y> - c++

I have seen a code somewhere in which a guy did something like this
#define pp pair<int,int>
int main()
{
int n,i;
scanf("%d",&n);
vector<pp> G;
for(i=0;i<n;i++)
{
int x,y;
scanf("%d%d",&x,&y);
G.push_back(pp(x+y,x-y));
}
sort(G.begin(),G.end());
I want to know how sorting is done here. I mean to say on what parameter sorting is performed in a vector that contains pairs in it.

The sort function sort according to an comparaison function (which defines the order). One could want to specify which omparaison to use, however if the comparaison function is not specified (like in your case) then sort uses the defaut order being the < operator on the considered object
So in your case, ordering is possible because the comparaison operator is overloaded for std::pair.
The behaviour of those operator is described here : http://en.cppreference.com/w/cpp/utility/pair/operator_cmp
Therefor, when sorting the < operator on the std::pair class will be called, and your pairs are going to be ordered lexicographically
(0,1) < (0,2) < (1,0) < (1,2) < (2,7)

Check this link: http://www.cplusplus.com/reference/utility/pair/operators/
Similarly, operators <, >, <= and >= perform a lexicographical comparison on the sequence formed by members first and second.
So, a < b means (a.first < b.first) || (a.first == b.first && a.second < b.second)

Related

How does comparator function of c++ STL sort work?

bool sortbysec(const pair<int,int> &a,const pair<int,int> &b)
{
return (a.second < b.second);
}
sort(vect.begin(), vect.end(), sortbysec);
vector< pair <int, int> > vect;
int arr[] = {10, 17, 5, 70 };
int arr1[] = {30, 60, 20, 50};
int n = sizeof(arr)/sizeof(arr[0]);
for (int i=0; i<n; i++)
vect.push_back( make_pair(arr[i],arr1[i]));
what does return(a.second<b.second) mean?
how is it sorting by the second element?
The concept of a sorted sequence s, in abstract, is that for any pair of elements s[i] and s[j], s[i] is not greater than s[j] when i is less than j.
Sorting a sequence is simply rearranging the elements of the sequence to satisfy this definition. Therefore, in order to sort elements, we need to be able to ask if a particular value is less than or greater than some other value -- otherwise we cannot be sure that our arrangement of the sequence satisfies the definition of a "sorted sequence."
std::sort takes a comparison function as a means of answering this question. By default, it is std::less<T>() which simply uses the operator < to compare two elements. By applying this function to two elements, it can determine if they need to be rearranged. Looking at our definition of a sorted sequence, if s[j] < s[i] when i < j then the definition is not met. Swapping those two elements corrects the problem for that specific pair of elements.
By applying this comparison function along with a sorting algorithm, std::sort is able to determine the order that the elements should be in for the sequence to be sorted. That's all the sort function does: it applies this comparison function to pairs of elements and rearranges them until the sequence is sorted.
You can supply any comparison function fn that has strict weak ordering and std::sort will rearrange the elements as necessary to ensure that !fn(s[i], s[j]) is true for all valid index pairs i and j where i > j. This allows you to manipulate the sort function to get specific orders. For example:
If you supply a function that compares using the > operator instead of the < operator, then the sorted sequence will be in descending order.
You can supply a function that compares a specific attribute of the values. If you have a sequence of struct Person { std::string name; int age; } values, you could have a function that compares the ages only, which will sort the sequence by the age attribute.
You could even sort on multiple attributes. If you compare age first and then compare name if the ages are equal, the sequence will be sorted by age -- but within each subsequence where the age is equal, that subsequence is sorted by name.

Sorting by a Object field in a Vector of Vector of Objects

I have a vector<vector<Edge> >graph in my Edge object i have 3 int fields called height weight length. I have already populated my vector with elements and now i want to sort my vector in terms of weight from largest to smallest. How would i be able to do so.
Assuming Edge looks something like
struct Edge {
int height, weight, length;
}
Add a function like
bool compareWeight(const Edge& l, const Edge& r) {
return l.weight > r.weight;
}
And use sort from algorithm
for(int i = 0; i < graph.size(); i++) {
std::sort(graph[i].begin(), graph[i].end(), compareWeight);
}
That will sort each vector of Edge's by weight.
To do so your class Edge must have overloaded operator <.
And then you can just use sort() from #include <algorithm>
sort(graph.begin(), graph.end());
Also make sense to overload other operators as >, >=, <=, ==, !=.

How does a priority queue of pair<int,int> work even without specifying a custom comparator?

A std::priority_queue uses a std::vector as the default container (Reference this). For sorting on the basis of the first element in a std::vector<pair<int, int>>, we need to define our own comparison function (Reference this). This is what I understand.
Now, the following code returns the k most frequent elements in a non-empty array, in O(NlogK):
class Solution {
public:
vector<int> topKFrequent(vector<int>& nums, int k) {
if(nums.empty())
return vector<int>();
unordered_map< int, int > hashMap;
for(int i=0; i<nums.size(); i++)
hashMap[nums[i]]++;
priority_queue< pair< int, int >> pq;
vector< int > result;
unordered_map< int, int >::iterator it=hashMap.begin();
for(it=hashMap.begin(); it!=hashMap.end(); it++) {
//the first one is frequency and the second one is the value
pq.push(make_pair(it->second, it->first));
//the peculiar implementation below is because we the code to be O(NlogK)
if(pq.size()>(hashMap.size()-k)) {
result.push_back(pq.top().second);
pq.pop();
}
}
return result;
}
};
This code works correctly and gets accepted by the judge - but how? The std::priority_queue, using a std::vector<pair<int, int>> as its underlying container must contain a custom comparison function so that it sorts correctly. So, how does it work?
Frankly, it works because it is designed to do so.
A few things:
a std::priority_queue employs std::less<T>, where T is the underlying sequence value type, as the default comparator when no override is specified.
std::less<T> invokes operator < against two T arguments, resolving to whatever best-fits and/or is available.
Therefore, if this works as you desired with no special override of the sequence type comparator, it must mean that there exists an operator < for std::pair<int,int> that wire this whole thing together.
And indeed there is. Checking the documentation for std::pair<T1,T2>, you'll find there is an operator < overload that effectively does this:
if (lhs.first < rhs.first)
return true;
else if (!(rhs.first < lhs.first))
return lhs.second < rhs.second
else
return false;
Mind-play examples of how this works are left to the reader to think about.

How does std::set comparator function work?

Currently working on an algorithm problems using set.
set<string> mySet;
mySet.insert("(())()");
mySet.insert("()()()");
//print mySet:
(())()
()()()
Ok great, as expected.
However if I put a comp function that sorts the set by its length, I only get 1 result back.
struct size_comp
{
bool operator()(const string& a, const string& b) const{
return a.size()>b.size();
}
};
set<string, size_comp> mySet;
mySet.insert("(())()");
mySet.insert("()()()");
//print myset
(())()
Can someone explain to me why?
I tried using a multi set, but its appending duplicates.
multiset<string,size_comp> mSet;
mSet.insert("(())()");
mSet.insert("()()()");
mSet.insert("()()()");
//print mset
"(())()","()()()","()()()"
std::set stores unique values only. Two values a,b are considered equivalent if and only if
!comp(a,b) && !comp(b,a)
or in everyday language, if a is not smaller than b and b is not smaller than a. In particular, only this criterion is used to check for equality, the normal operator== is not considered at all.
So with your comparator, the set can only contain one string of length n for every n.
If you want to allow multiple values that are equivalent under your comparison, use std::multiset. This will of course also allow exact duplicates, again, under your comparator, "asdf" is just as equivalent to "aaaa" as it is to "asdf".
If that does not make sense for your problem, you need to come up with either a different comparator that induces a proper notion of equality or use another data structure.
A quick fix to get the behavior you probably want (correct me if I'm wrong) would be introducing a secondary comparison criterion like the normal operator>. That way, we sort by length first, but are still able to distinguish between different strings of the same length.
struct size_comp
{
bool operator()(const string& a, const string& b) const{
if (a.size() != b.size())
return a.size() > b.size();
return a > b;
}
};
The comparator template argument, which defaults to std::less<T>, must represent a strict weak ordering relation between values in its domain.
This kind of relation has some requirements:
it's not reflexive (x < x yields false)
it's asymmetric (x < y implies that y < x is false)
it's transitive (x < y && y < z implies x < z)
Taking this further we can define equivalence between values in term of this relation, because if !(x < y) && !(y < x) then it must hold that x == y.
In your situation you have that ∀ x, y such that x.size() == y.size(), then both comp(x,y) == false && comp(y,x) == false, so since no x or y is lesser than the other, then they must be equal.
This equivalence is used to determine if two items correspond to the same, thus ignoring second insertion in your example.
To fix this you must make sure that your comparator never returns false for both comp(x,y) and comp(y,x) if you don't want to consider x equal to y, for example by doing
auto cmp = [](const string& a, const string& b) {
if (a.size() != b.size())
return a.size() > b.size();
else
return std::less()(a, b);
}
So that for input of same length you fallback to normal lexicographic order.
This is because equality of elements is defined by the comparator. An element is considered equal to another if and only if !comp(a, b) && !comp(b, a).
Since the length of "(())()" is not greater, nor lesser than the length of "()()()", they are considered equal by your comparator. There can be only unique elements in a std::set, and an equivalent object will overwrite the existing one.
The default comparator uses operator<, which in the case of strings, performs lexicographical ordering.
I tried using a multi set, but its appending duplicates.
Multiset indeed does allow duplicates. Therefore both strings will be contained despite having the same length.
size_comp considers only the length of the strings. The default comparison operator uses lexicographic comparison, which distinguishes based on the content of the string as well as the length.

priority_queues in C++

I'm a bit of a noob to iterators. I'm trying to create a priority_queue, sorted by vector length. (I.e., I want to pop off the longest vectors in order.)
This is the resource that I've been using:
http://www.cplusplus.com/reference/stl/priority_queue/priority_queue/
I tried this code, and it seems to do what I want:
// testing to make sure that a priority queue will always give me the longest vector
priority_queue< vector<int> > q;
vector<int> f;
f.push_back(1);
vector<int> g;
g.push_back(19);
g.push_back(80);
vector<int> y;
y.push_back(62);
y.push_back(10);
y.push_back(11);
q.push(f);
q.push(g);
q.push(y);
vector<int> out = q.top();
for (unsigned int i = 0; i < out.size(); i++) {
cout << out[i] << endl;
}
My questions:
1. Will this always give me the longest vector? This seems to be the case.
2. If not, what else should I do? The iterator syntax on the reference page is like... o_O
Thanks!!
No, the code doesn't do what you expect. It compares the vectors lexicographically rather than by length. To compare by length use a custom comparator:
struct LengthCompare {
bool operator() (const vector<int>& a, const vector<int>& b) {
return a.size() < b.size();
}
};
priority_queue<vector<int>, vector<vector<int> >, LengthCompare> q;
Also note that your queue stores copies of the vectors, which might be not so efficient because it may copy them when it builds the heap. Store (smart) pointers instead.
priority_queues in C++ use a Comparison object to determine what is the greatest element. By default, this is the < (less-than) operator over the objects held in the priority_queue - so you need to know what < means over vectors. This page http://www.cplusplus.com/reference/stl/vector/operators/ has some information about that.