How to sort a set of pairs? - c++

How shall I use the qsort function to sort a set of pairs ?
This is my set :
set< pair< int, int> > my_set
This I guess should be my compare function:
int compare (const void * a, const void * b)
{
return ( *(int*)a - *(int*)b );
}
Should my qsort look like this?
qsort (my_set, no_of_pairs, sizeof(int), compare);
When I sort them I want to sort by the values of a bi-dimensional array **M;, where
M[my_set.first][my_set.second] = the_value

First, prefer std::sort to qsort with c++ std containers.
Secondly, you cannot sort a std::set a posteriori. std::set is sorted.
You can however specify a custom sorting for the std::set at instanciation using a 2nd template parameter.
Refer to the specs.
What you could do, if you need to sort the data after the fact, is use a std::vector instead. There is an algorithm that will cull the duplicate value.
This proposed solution assumes M is some global variable.
#include <algorithm>
bool less_than(const std::pair<int, int> &some, const std::pair<int, int> &other) {
return M[some.first][some.second] < M[other.first][other.second];
}
std::sort(yourvector.begin(), yourvector.end(), less_than);
If M isn't a global variable, you could do something like that :
struc Compair { // see what I did there ? #sofunny
bool operator() (const std::pair<int, int> &some, const std::pair<int, int> &other) {
return M[some.first][some.second] < M[other.first][other.second];
}
int **M;
}
then in main :
Compair mycomparefunctor;
mycomparefunctor.M = arr; // arr is the original int **
std::sort(yourvector.begin(), yourvector.end(), mycomparefunctor);
EDIT :
If you must use a std::set then you should define the custom ordering when you declare it, like so :
Compair mypredicate;
mypredicate.M = arr; // arr is the original int **
std::set<std::pair<int, int>, mypredicate> myset;
// add stuff to the set. They will be sorted following your predicate.
Be careful though : in a set you cannot add 2 items that compare equal. So if your int ** 2D array has several values that are equal, you won't be able to have several pairs corresponding to indexes of equal value in the set.

You're going about this fairly wrong.
Let's assume that we know the maximum value for each member of the pair. If you don't know this, then you need to figure it out. I'm going to assume that it is 100.
Then all we need to do is iterate over the set, and insert them into the new array. There's no faster way to go about this.
int M[100][100] = {};
for (auto const & pair : my_set)
M[pair.first][pair.second] = the_value;

Related

Sorting a vector by unordered map of the elements pointers as keys

I have a vector of elements std::vector<T> my_vec. At some point in my code I assign a score for each element of the vector using an unordered map. After that, I would like to sort the vector by the scores of its elements with the minimum code possible.
I came up with this solution, define the map as follows: std::unordered_map<const T*, float> scores_map. For score assignment, insert the score to the map as follows:
for (const auto& el : my_vec)
scores_map[&el] = calc_score(el);
Then I sort using:
std::sort(my_vec.begin(), my_vec.end(),
[&my_map](const auto& a, const auto& b){return my_map[&a] > my_map[&b];});
Is this considered a bug-free and good practice, if not any idea how to make it so?
#fas wrote in a comment:
Elements in vector are moved during sort, so their pointers also change and scores_map becomes invalid, isn't it?
That is correct. You should not use pointers as keys in scores_map.
Option 1
If the vector contains unique items, you may use the T as the key type.
for (const auto& el : my_vec)
scores_map[el] = calc_score(el);
Then sort using:
std::sort(my_vec.begin(), my_vec.end(),
[&my_map](const auto& a, const auto& b){return my_map[a] > my_map[b];});
Option 2
If the vector does not contain unique elements, you may use the following strategy.
Use indices as the key of my_map.
Create a helper std::vector<size_t> object that contains just indices.
Sort the vector of indices.
Use the sorted indices vector to fetch the elements from my_vec.
for (size_t i = 0; i < my_vec.size(); ++i )
scores_map[i] = calc_score(my_vec[i]);
// Create the vector of indices
std::vector<size_t> indices_vec(my_vec.size());
for ( size_t i = 0; i < indices_vec.size(); ++i )
{
indices_vec[i] = i;
}
// Sort the vector of indices
std::sort(indices_vec.begin(), indices_vec.end(),
[&my_map](size_t a, size_t b){return my_map[a] > my_map[b];});
for (auto index : indices_vec)
{
// Use my_vec[index]
}
No, this not bug-free. std::sort will change the addresses of the elements.
You could store the score with each element in a pair:
std::pair<float, T>
and sort the vector
std::vector<std::pair<float, T> > my_vec
with
std::sort(my_vec.begin(), my_vec.end(),
[](const auto& a, const auto& b){return a.first > b.first;});

Pushing std::count result into vector

I want to create std::map with "T,int" but manually. I've copied std::set of data into clear vector, because I want to have an access to elements of unique elements. Here is a following code.
template<typename T>
void foo<T>::function()
{
std::set<T> unique_set(A.begin(), A.end());
std::copy(unique_set.begin(), unique_set.end(), std::inserter(vector_uniq, vector_uniq.begin()));
for (const auto&elem : vector_uniq)
{
counter = std::count(A.begin(), A.end(), elem); // declared in header as unsigned int
}
}
Is it possible to push value of counter to another vector ? Then I would have two vectors of value and their occurences and those two vectors will be used in another function. That's why I would like to have separated containers. Thank's for all responses.
Set up your second vector for the count results:
for (const auto&elem : vector_uniq) {
auto counter = std::count(A.begin(), A.end(), elem); // declared in header as unsigned int
vector_count.push_back(counter);
}

Sorting vector of arrays by array's element

I have encountered a problem when writing algorithm for solving Knapsack problem. I have a vector of 3-element arrays (C++11) and I want to sort the vector by the value of let's say first element of these arrays.
I've tried std::sort with predefined compare function, but it doesn't even compile.
I guess my compare function doesn't work as I expect:
bool compareByValue(const data &a, const data &b)
{
return a[0] < b[0];
}
int main()
{
vector<array<int, 3> > myVector;
...
sort ( myVector.begin(), myVector.end(), compareByValue );
}
It's not the first time when I have the similar problem, I tried to find solution on the Net, but without any satisfying result.
Also note that std::array has overloaded comparison operators, which compare arrays lexicographically. That means, if you want to sort based on the first element, you don't even need a predicate. Just std::sort your vector.
I don't know where you got data from, but you need to change it to array<int, 3> or make compareByValue a template:
bool compareByValue(const array<int, 3> &a, const array<int, 3>&b)
{
return a[0] < b[0];
}

Sorting a std::map

I have defined a map like this
typedef std::vector< int > aVector;
typedef std::map< int, aVector > aMap;
aMap theMap;
Assume that the map finally contains some elements like this
10 [0 3 7] size=3
12 [40 2 30 3 10] size=5
20 [5 10] size=2
25 [6] size=1
I want to sort on the size of the vector (e.g theMap->second.size()). So the result will be
5 3 2 1
What is the fastest way to do that? The basic idea is to push the sizes on another vector and then call sort(), like this
aVector v, sorted;
aMap::iterator it = theMap.begin();
for (; it != theMap.end(); ++it) {
v.push_back(it->second.size());
}
// using std sort!!
Is there any better option?
Why not putting the vector as the key and using your custom key comparison function / functor which would compare the keys sizes ?
You can see examples of this in http://www.cplusplus.com/reference/map/map/map/ ?
I haven't acces to a C++ compiler right now, but it would be something like:
#include <map>
struct aComparisonStruct {
bool operator() (const aVector& lhs, const aVector& rhs) const {
return lhs.size > rhs.size;
}
};
int main () {
typedef std::vector<int> aVector;
typedef std::map<aVector, int, aComparisonStruct> aMap;
// Use your map
return 0;
}
There is a problem though : You can't use the property of single key presence anymore, and you wouldn't be able to add multiple times the same vector. Maybe another implementation would be more appropriate ?
Also, it would be definitely better to use pointers as keys, but since I can't compile, I don't want to mix up pointers and references and give you something that wouldn't probably work.
There is a very common task when you need to have quick lookup through std::map or std::hash_map and to manage it with some specific order. In this situation you may use kinda "index" collection over your main collection:
aMap theMap;
std::map<size_t, std::list<aMap::iterator> > sizes;
// add item
auto r = theMap.insert(key, std::vector<int>());
if (!r->second)
{
sizes[r->first->second.size()].remove(r->first);
}
r->first->second->push_back(item);
sizes[r->first->second.size()].push_back(r->first);
Std sort is declared as this:
void sort (RandomAccessIterator first, RandomAccessIterator last, Compare comp);
So you can pass in a comp:
struct comp {
bool operator() (const aVector& lhs, const aVector& rhs) {
return (lhs.size() < rhs.size());
}
} mycomp;
std::sort(theMap, comp)
Note that it is better to pass the comp as an input rather than fix it when you declare the map.
In this case you don't have to declare different maps when you only want to change the comp.

std::sort to sort an array and a list of index?

I have a function that takes two vectors of the same size as parameters :
void mysort(std::vector<double>& data, std::vector<unsigned int>& index)
{
// For example :
// The data vector contains : 9.8 1.2 10.5 -4.3
// The index vector contains : 0 1 2 3
// The goal is to obtain for the data : -4.3 1.2 9.8 10.5
// The goal is to obtain for the index : 3 1 0 2
// Using std::sort and minimizing copies
}
How to solve that problem minimizing the number of required copies ?
An obvious way would be to make a single vector of std::pair<double, unsigned int> and specify the comparator by [](std::pair<double, unsigned int> x, std::pair<double, unsigned int> y){return x.first < y.first;} and then to copy the results in the two original vectors but it would not be efficient.
Note : the signature of the function is fixed, and I cannot pass a single vector of std::pair.
Inside the function, make a vector positions = [0,1,2,3...]
Sort positions with the comparator (int x, int y){return data[x]<data[y];}.
Then iterate over positions , doing result.push_back(index[*it]);
This assumes the values in index can be arbitrary. If it is guaranteed to already be [0,1,2..] as in your example, then you don't to make the positions array, just use index in it's place and skip the last copy.
http://www.boost.org/doc/libs/1_52_0/libs/iterator/doc/index.html#iterator-facade-and-adaptor
Write a iterator over std::pair<double&, signed int&> that actually wraps a pair of iterators into each vector. The only tricky part is making sure that std::sort realizes that the result is a random access iterator.
If you can't use boost, just write the equivalent yourself.
Before doing this, determine if it is worth your bother. A zip, sort and unzip is easier to write, and programmer time can be exchanged for performance in lots of spots: until you konw where it is optimally spent, maybe you should just do a good-enough job and then benchmark where you need to speed things up.
You can use a custom iterator class, which iterates over both vectors in parallel. Its internal members would consist of
Two references (or pointers), one for each vector
An index indicating the current position
The value type of the iterator should be a pair<double, unsigned>. This is because std::sort will not only swap items, but in some cases also temporarily store single values. I wrote more details about this in section 3 of this question.
The reference type has to be some class which again holds references to both vectors and a current index. So you might make the reference type the same as the iterator type, if you are careful. The operator= of the reference type must allow assignment from the value type. And the swap function should be specialized for this reference, to allow swapping such list items in place, by swapping for both lists separately.
You can use a functor class to hold a reference to the value array and use it as the comparator to sort the index array. Then copy the values to a new value array and swap the contents.
struct Comparator
{
Comparator(const std::vector<double> & data) : m_data(data) {}
bool operator()(int left, int right) const { return data[left] < data[right]; }
const std::vector<double> & m_data;
};
void mysort(std::vector<double>& data, std::vector<unsigned int>& index)
{
std::sort(index.begin(), index.end(), Comparator(data));
std::vector<double> result;
result.reserve(data.size());
for (std::vector<int>::iterator it = index.begin(), e = index.end(); it != e; ++it)
result.push_back(data[*it]);
data.swap(result);
}
This should do it:
std::sort(index.begin(), index.end(), [&data](unsigned i1, unsigned i2)->bool
{ return data[i1]<data[i2]; });
std::sort(data.begin(), data.end());