Reputation: 3125
I have three std::vectors
, each containing different data types.
What I need to do is remove the same index item from each, depending on the value of that indexed item in the first one.
In the code below, if the value of localMap_points[i].m_counter
is more than 30, I erase the item at index [i]
from all three vectors. (localMap_desc
contains 8 items for the other vectors one, hence the x 8
added to that one)
This works perfectly, but is slow. Is there a faster way to do this?
I have:
for (int i = 0; i < localMap_points.size(); i++)
{
if (localMap_points[i].m_counter > 30)
{
localMap_kp.erase(localMap_kp.begin() + i, localMap_kp.begin() + i + 1); // Deleting from n element to n element
localMap_desc.erase(localMap_desc.begin() + (i * 8), localMap_desc.begin() + (i * 8) + 8); // Deleting from n element to n element X 8
localMap_points.erase(localMap_points.begin() + i, localMap_points.begin() + i + 1); // Deleting from n element to n element
}
}
Upvotes: 3
Views: 492
Reputation: 85351
Here's an example how you can apply the erase-remove idiom for three vectors at once.
The algorithm in O(N), i.e. requires a single pass over the vectors.
template<typename T1, typename T2, typename T3, typename P>
void erase3(T1& v1, T2& v2, T3& v3, P predicate) {
auto first1 = begin(v1);
auto first2 = begin(v2);
auto first3 = begin(v3);
while (first1 != end(v1) && !predicate(*first1)) {
++first1; ++first2; ++first3;
}
if (first1 != end(v1)) {
auto it1 = first1; auto it2 = first2; auto it3 = first3;
while (++it1 != end(v1)) {
++it2;
++it3;
if (!predicate(*it1)) {
*first1++ = std::move(*it1);
*first2++ = std::move(*it2);
*first3++ = std::move(*it3);
}
}
v1.erase(first1, end(v1));
v2.erase(first2, end(v2));
v3.erase(first3, end(v3));
}
}
int main()
{
std::vector<int> v1 = { 1,2,3,4,5 }, v2 = { 11,12,13,14,15 }, v3 = { 21,22,23,24,25 };
erase3(v1, v2, v3, [](int a) { return a == 3 || a == 4; });
}
This works by moving all remaining elements to the beginning of the vectors and then trimming off the moved-out ones from the end.
Upvotes: 2
Reputation: 2014
Use a more proper data structure. vectors are not meant for fast internal deletion and insertion (the are O(n)
), lists may be a better choice if you don't need indexed access (insert/delete is O(1)
== constant time). Depending by your needs, you may use an helper structure, but it's not possible without further information.
--- Adding another thought (someone already suggested this though).
If order of vector doesn't matter, you could swap
the item to be deleted with the last in the vector, and then pop_back()
.
It would be O(1)
.
--- Thinking out loud.
Another trick would be to use a priority queue. They are data structure that reorders itself based on the "weight" of one field, in your application it would be the counter
field. they are able to insert in O(log n)
and delete from top in O(1)
. So it would be quite fast to remove the items with higher counter
, because they are always on top.
Upvotes: 2
Reputation: 38277
The performance bottleneck here is the memory layout of std::vector
, possibly together with the special member function properties/existence of the vector elements. If you erase one element in the middle, all the elements from this position until the end must be moved to adjust to the element before the removed one. This is done via
noexcept
The first thing to ensure is hence that the element type stored in the vector has a noexcept
move constructor.
Second, make sure to use the erase-remove idiom here. By following this pattern, the reordering in terms of swap invocations is done first, before any calls to erase. For n
items to be erased, those are n
swap invocations. Then, you will have a trivial call to std::vector::erase
, as all elements are already in place as they should. For this procedure to as fast as possible, you might want to consider providing a swap
function for your custom types.
Upvotes: 4