Reputation: 55
I have a vector of structures, for which I have overloaded all comparison operators for. I do not know the size of the structure at compilation time.
What's the fasted way to retrieve the n best (where "best" can be either the smallest or the largest) elements in the vector? I am aware of max_element and min_element but they only return a single element. I would prefer to not loop n times, retrieving the best element, removing it and then getting the next one. This approach seems too slow.
Thanks.
Upvotes: 1
Views: 2384
Reputation: 3651
The other answers here work, but you could also use copy_if.
std::vector<int> vec{ 1, 5, 7, 9, 3, 6, 8, 4, 2 };
std::vector<int> odd;
std::copy_if(vec.begin(), vec.end(), std::back_inserter(odd), [](int a){ return a % 2 == 1; });
std::vector<int> low;
std::copy_if(vec.begin(), vec.end(), std::back_inserter(low), [](int a){ return a <= 3; });
std::vector<int> high;
std::copy_if(vec.begin(), vec.end(), std::back_inserter(high), [](int a){ return a >= 7; });
There is also remove_copy_if if you need to remove the matching data.
Upvotes: 0
Reputation: 73366
I propose you a solution that does neither modify nor make a copy of the vector:
So I assume that the original vector is very very big and you still need the data in the original order for other purposes
template <typename T>
set<T> best_n2(vector<T>& v, size_t n)
{
set<int> u; // the result is a sorted set
int i = 0;
for (auto x : v) { // fill the N first elements
if (i++ < n) {
u.insert(x);
}
else if (x > *u.begin()) { // then get rid of the smallest if necessray
u.erase(*u.begin());
u.insert(x);
}
}
return u;
}
If you can modify the original vector, I'd choose the solution of szulack. I didn't propose it because he types much faster than I do ;-)
Upvotes: 0
Reputation: 206567
If you want to retrieve the n
smallest elements sort the vector
in ascending order.
If you want to retrieve the n
largest elements sort the vector
in descending order.
Then,
Get two iterators.
start = v.begin();
end = start + n;
Iterate from start
till end
but excluding end
.
Upvotes: 0
Reputation: 11181
You can use std::nth_element
, that will move the smallest n
elements to the first n
positions in the range (notice that the relative ordering of those n
elements is not defined).
std::vector<T> objects;
std::nth_element( objects.begin(), objects.begin() + n, objects.end() );
// Now the range [objects.begin(), objects.begin() + n) contains the lowest n elements
// Obviously n must be <= objects.size()
When you write that you do not know the size of the structure at compilation time, I assume that you have a collection of polymorphic objects, and you have a vector of pointers instead of elements. No big deal, you can still use std::nth_element
with a lambda.
std::vector<T*> objects;
std::nth_element( objects.begin(),
objects.begin() + n,
objects.end(),
[](const T * lhs, const T * rhs)
{
return (*lhs) < (*rhs); // Or (*lhs) > (*rhs) for the greatest n elements
});
Upvotes: 5
Reputation: 15468
Another approach than the brute-force sorting-and-taking-the-first: Set up a temporary container of size n
and initialize it with the first n
elements of you original vector (which has size N
, say). Sort that temporary vector.
Next traverse your original vector and insert elements (using std::lower_bound
) whenever they are larger than the current minimum of your sorted container (and at the same time, throw out the current minimum). This should first take O(n log n + N)
instead of O(N log N)
asymptotically, but more important, also the constant prefactors are likely much smaller.
Upvotes: 1
Reputation: 703
Sort it and take n first elements.
struct Foo { };
std::vector<Foo> v = { Foo(), Foo(), Foo() };
std::sort(v.begin(), v.end());
std::vector<Foo> v_best(v.begin(), v.begin() + n); //where n is your best count
Upvotes: 1