I am using vector pair to sort where I am inserting first and second element using a loop
vect.push_back(make_pair(count[i],arr[i]));
sort(vect.begin(),vect.end());
cout<<vect[i].second<<" ";
if my counts are equal then I want to print the second element in the order they were inserted in vector,
but it is not happening.
Can someome tell me why?
From std::sort
Sorts the elements in the range [first, last) in ascending order. The order of equal elements is not guaranteed to be preserved.
1) Elements are compared using operator<.
and from std::pair::operator<
3) If lhs.first < rhs.first, returns true. Otherwise, if rhs.first < lhs.first, returns false. Otherwise, if lhs.second < rhs.second, returns true. Otherwise, returns false.
So effectively, sort compares count[i], and if two of them are equal, it compares arr[i].
If you want to compare the counts only and keep the original order for equal keys, you must use std::stable_sort(first, last, comp)
Sorts the elements in the range [first, last) in ascending order. The order of equal elements is guaranteed to be preserved.
with an appropriate comparison operator
std::stable_sort(vect.begin(), vect.end(),
[](const std::pair<int, T> &x, const std::pair<int, T> &y) {
return x.first < y.first;
}
);
Related
I've been experimenting with the "lower_bound()/upper_bound()" functions in C++ w.r.t. arrays/vectors, and I get incorrect results when applying custom compare operators to the function.
My current understanding (based on https://www.cplusplus.com/reference/algorithm/upper_bound/) is that when you search for some value 'val' (of any datatype) in an array, it returns the first iterator position "it" in the array (from left to right) that satisfies !comp(val,*it), is this wrong? If so, how exactly does the searching work?
P.S. In addition, what is the difference of using lowerbound/upperbound when your searching criterion is a specific boolean compare function?
Here is an example that produced erroneous results:
auto comp2 = [&](int num, pair<int,int>& p2){return num>p2.second;};
vector<pair<int,int>> pairs = {{1,2},{2,3},{3,4}}; //this array should be binary-searchable with 'comp2' comparator, since pairs[i].second is monotonously increasing
int pos2 = upper_bound(pairs.begin(),pairs.end(),2,comp2)-pairs.begin();
cout<<pos2<<endl; //outputs 3, but should give 0 because !comp2(2,arr[0]) is true, and arr[0] is the ealiest element in the array
Thanks!
I think most (If not all) of the comparator functions are less, it can be std::less or something similar. So when we provide a custom comp function, we have to provide the less logic and think of it as less.
Now back to the upper_bound, it returns the first element greater than the value, which means our less should return true for it to stop (As Francois pointed out). While our comp function always returns false.
And your understanding about !comp(val,*it) is also not correct. It is the condition to continue the search, not to stop it.
Here is an example implementation of the upper_bound, let's take a look:
template<class ForwardIt, class T, class Compare>
ForwardIt upper_bound(ForwardIt first, ForwardIt last, const T& value, Compare comp)
{
ForwardIt it;
typename std::iterator_traits<ForwardIt>::difference_type count, step;
count = std::distance(first, last);
while (count > 0) {
it = first;
step = count / 2;
std::advance(it, step);
if (!comp(value, *it)) {
first = ++it;
count -= step + 1;
}
else
count = step;
}
return first;
}
You can see, if (!comp(value, *it)) is when the less return false, it means the value is greater than the current item, it will move forward and continue from the next item. (Because the items are increasing).
In the other case, it will try to reduce the search distance (By half the count) and hope to find earlier item that is greater than value.
Summary: You have to provide comp as less logic and let the upper_bound do the rest.
upper_bound returns the first element that satisfies comp(val, *it). In the link you provided, it shows
template <class ForwardIterator, class T>
ForwardIterator upper_bound (ForwardIterator first, ForwardIterator last, const T& val)
{
ForwardIterator it;
iterator_traits<ForwardIterator>::difference_type count, step;
count = std::distance(first,last);
while (count>0)
{
it = first; step=count/2; std::advance (it,step);
if (!(val<*it)) // or: if (!comp(val,*it)), for version (2)
{ first=++it; count-=step+1; }
else count=step;
}
return first;
}
Returns an iterator pointing to the first element in the range [first,last) which compares greater than val.
The searching works by starting at position 0(first). It then uses count to see the range of values it needs to check. It checks the middle of the range (first+count/2), and if that does not satisfy the condition, that position is now first (discarding all values before it), and repeats with the new first and range. If it does satisfy the condition, then the algorithm can discard all values after that, and repeat with the new range. When the range drops to 0, the algorithm can end. It assumes that if arr[5] is false, arr[0], arr[1] ... arr[4] are also false. Same with if arr[8] is true, arr[9], arr[10] ... arr[n] are also true.
The reason your code does not work is because the comparator used returns num>p2.second, meaning it looks for a value of p2.second that is less than num. Since you put in 2 for num, and there is no p2.second less than that in the vector, the output points to a position outside of the vector because it didn't find anything.
The difference between upper_bound and lower_bound is that upper_bound looks for the first value that satisfies the condition, while lower_bound looks for the first value that does not satisfy the condition. So
lower_bound(v.begin(), v.end(), val, [](int it, int val) {return !(val < it);});
is the same as
upper_bound(v.begin(), v.end(), val, [](int val, int it){return val < it;});
Note that for lower_bound, the comparator used takes (*it, val), not (val, *it).
I guess the only difference is how easy it is to frame the comparator in those terms - realizing that a<b is the same as not a>=b.
More explained here. I liked the explanation that said it finds [lower_bound, upper_bound) when using the same comparator.
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.
I was wondering if there's a way to sort my list of pairs based on the second element. Here is a code:
std::list<std::pair<std::string, unsigned int>> words;
words.push_back(std::make_pair("aba", 23);
words.push_back(std::make_pair("ab", 20);
words.push_back(std::make_pair("aBa", 15);
words.push_back(std::make_pair("acC", 8);
words.push_back(std::make_pair("aaa", 23);
I would like to sort my list words based in the integer element in decreasing order so that my list would be like:
<"aba", 23>,<"aaa", 23>,<"ab", 20>,<"aBa", 15>,<"acC", 8>
Also, is it possible to sort them by both the first and second element such that it sorts by the second elements first (by integer value), and then if there's two or more pairs with the same second element (i.e. same integer value), then it will sort those based on the first element in alphabetical order, then the first 2 pairs on my sorted list above would swap, so:
<"aaa", 23>,<"aba", 23>,<"ab", 20>,<"aBa", 15>,<"acC", 8>
I would like to sort my list words based in the integer element in decreasing order
The sorting predicate must return true if the first element (i.e., the first pair) passed precedes the second one in your established order:
words.sort([](auto const& a, auto const& b) {
return a.second > b.second;
});
Since you want to sort the list in decreasing order, the pair a will precede b if its second element (i.e., the int) is greater than b's second element.
Note that std::sort() doesn't work for sorting an std::list because it requires random access iterators but std::list only provides bidirectional iterators.
is it possible to sort them by both the first and second element such that it sorts by the second elements first (by integer value), and then if there's two or more pairs with the same second element (i.e. same integer value), then it will sort those based on the first element in alphabetical order
Assuming again decreasing order for the int element, just resort to the second element of the pairs when both int elements are the same:
lst.sort([](auto const& a, auto const& b) {
if (a.second > b.second)
return true;
if (a.second < b.second)
return false;
return a.first < b.first;
});
or more concise thanks to std::tie():
lst.sort([](auto const& a, auto const& b) {
return std::tie(b.second, a.first) < std::tie(a.second, a.first);
});
std::list has a member function std::list::sort that is supposed to do the sorting.
One of its two overloads accepts custom comparison function:
template <class Compare>
void sort(Compare comp);
which you can use as follows:
words.sort([](const std::pair<string, unsigned int> &x,
const std::pair<string, unsigned int> &y)
{
return x.second > y.second;
});
I have a vector which contains lot of elements of my class X .
I need to find the first occurrence of an element in this vector say S such that S.attrribute1 > someVariable. someVariable will not be fixed . How can I do binary_search for this ? (NOT c++11/c++14) . I can write std::binary_search with search function of greater (which ideally means check of equality) but that would be wrong ? Whats the right strategy for fast searching ?
A binary search can only be done if the vector is in sorted order according to the binary search's predicate, by definition.
So, unless all elements in your vector for which "S.attribute1 > someVariable" are located after all elements that are not, this is going to be a non-starter, right out of the gate.
If all elements in your vector are sorted in some other way, that "some other way" is the only binary search that can be implemented.
Assuming that they are, you must be using a comparator, of some sort, that specifies strict weak ordering on the attribute, in order to come up with your sorted vector in the first place:
class comparator {
public:
bool operator()(const your_class &a, const your_class &b) const
{
return a.attribute1 < b.attribute1;
}
};
The trick is that if you want to search using the attribute value alone, you need to use a comparator that can be used with std::binary_search which is defined as follows:
template< class ForwardIt, class T, class Compare >
bool binary_search( ForwardIt first, ForwardIt last,
const T& value, Compare comp );
For std::binary_search to succeed, the range [first, last) must be
at least partially ordered, i.e. it must satisfy all of the following
requirements:
for all elements, if element < value or comp(element, value) is true
then !(value < element) or !comp(value, element) is also true
So, the only requirement is that comp(value, element) and comp(element, value) needs to work. You can pass the attribute value for T, rather than the entire element in the vector to search for, as long as your comparator can deal with it:
class search_comparator {
public:
bool operator()(const your_class &a, const attribute_type &b) const
{
return a.attribute1 < b;
}
bool operator()(const attribute_type &a, const your_class &b) const
{
return a < b.attribute1;
}
};
Now, you should be able to use search_comparator instead of comparator, and do a binary search by the attribute value.
And, all bets are off, as I said, if the vector is not sorted by the given attribute. In that case, you'll need to use std::sort it explicitly, first, or come up with some custom container that keeps track of the vector elements, in the right order, separately and in addition to the main vector that holds them. Using pointers, perhaps, in which case you should be able to execute a binary search on the pointers themselves, using a similar search comparator, that looks at the pointers, instead.
For std::binary_search to succeed, the range need to be sorted.std::binary_search, std::lower_bound works on sorted containers. So every time you add a new element into your vector you need to keep it sorted.
For this purpose you can use std::lower_bound in your insertion:
class X;
class XCompare
{
public:
bool operator()(const X& first, const X& second) const
{
// your sorting logic
}
};
X value(...);
auto where = std::lower_bound(std::begin(vector), std::end(vector), value, XCompare());
vector.insert(where, value);
And again you can use std::lower_bound to search in your vector:
auto where = std::lower_bound(std::begin(vector), std::end(vector), searching_value, XCompare());
Don't forget to check if std::lower_bound was successful:
bool successed = where != std::end(vector) && !(XCompare()(value, *where));
Or directly use std::binary_search if you only want to know that element is in vector.
I have defined the following function object:
struct Predicate1
{
__device__ bool operator ()
(const DereferencedIteratorTuple& lhs, const DereferencedIteratorTuple& rhs)
{
using thrust::get;
//if you do <=, returns last occurence of largest element. < returns first
if (get<0>(lhs)== get<2>(lhs) && get<0>(lhs)!= 3) return get<1>(lhs) < get<1>(rhs);
else
return true ;
}
};
where the DereferencedIteratorTuple is as follows:
typedef thrust::tuple<int, float,int> DereferencedIteratorTuple;
Moreover, i call it as follows:
result = thrust::max_element(iter_begin, iter_end, Predicate1());
But the result is the tuple (3,.99,4). I am confused why this is the result because the condition get<0>(lhs)== get<2>(lhs) does not hold in the if for this tuple. Thus, the operator returns true for every comparison of this tuple. However, thrust::max_elementis defined as follows :
"This version compares objects using a function object comp.
Specifically, this version of max_element returns the first iterator i
in [first, last) such that, for every iterator j in [first, last),
comp(*i, *j) is false."
Thus, there is no way this should be chosen as for this tuple, operator never returns false. Please let me know what i am doing wrong
The predicate helps algorithm to determine which element to prefer. If predicate returns true the algorithm prefers rhs over lhs. If it return false the algorithm prefers lhs over rhs. In the case when predicate always returns true the algorithm will select last element in the array. This is true for both stl and thrust algorithms.
I guess, that your result never occured as lhs during comparison process and every time it was not filtered since rhs's second value was smaller than 0.99.
If you want to filter such values you'd better rewrite your predicate.