vector erase multiple regions, 2 x erase vs single assign? - c++

I need to erase 2 ranges of elements from vector, the vector looks like this:
std::vector<int> vec {0, 1, 2, 3, 4, 5, 6, 7};
for example I need to keep only numbers 3 and 4. (in reality this could be big objects)
There are 2 options:
1) take target elements and drop the rest:
vec.assign(vec.begin() + 3, vec.begin() + 5);
2) explicitly remove ranges:
vec.erase(vec.begin() + 6, vec.end()); // first end (it's cheap)
vec.erase(vec.begin(), vec.begin() + 3); // then beginning (reallocates elements)
Which one of these 2 options will perform faster?
Or is there even better way?

You can use a single std::copy followed by std::vector::erase. It's correct and likely uses memcpy or memmove for POD types so it should be fast. Something like:
auto itr = std::copy(vec.begin() + 3, vec.begin() + 5, vec.begin());
vec.erase(itr, vec.end());
If the vector elements are movable you could use the std::move algorithm instead of std::copy.

Related

Is there any standard variadic function for erasing multiple elements in a vector?

Take this vector:
std::vector<int> v = {1, 2, 3, 4, 5};
Let's say I want to remove some elements of a vector at some arbitrary indices: 0, 1, and 3. It's tedious to have to write something like this:
v.erase(v.begin());
v.erase(v.begin());
v.erase(v.begin() + 1);
Is there any standard function that takes in an arbitrary number of indices to erase from a vector? Something like this: v.erase(0, 1, 3);
Yes and no.
There's nothing that deals with indices. There's also nothing that deals with arbitrary elements.
But you can erase multiple items that form a contiguous range at once. So you can coalesce your first two calls to erase into one (and probably about double the speed in the process).
// erase the first two elements
v.erase(v.begin(), v.begin() + 2);
If you want to erase first three element so for this you can run.
std::vector<int> v = {1, 2, 3, 4, 5};
v.erase(v.begin(),v.begin()+3);
or,
v.erase(v.begin(),v.end()-2);

What is more efficient? vector.assign vs vector.erase

When a vector exists and tries to erase the back of that vector.
Is it efficient to use an 'vector.assign' in terms of time complexity? Or is it efficient to use 'vector.erase'?
Please let me know the time complexity in each case.
[For example]
vector<int> v = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
// 1. use assign
v.assign(v.begin(), v.begin() + 5);
// 2. use erase
v.erase(v.begin() + 5, v.end());
I would like to use vectors that consist only of elements from the beginning section of vectors to a certain index.
It's most efficient to use resize, because that's what that function is for.
For what its worth, self-assignment is not allowed for containers.

erase a specific value from a vector array [duplicate]

I'm trying to get a single element from a vector and push it to the back of the vector then remove it so I won't have an empty section in memory. The erase-remove idiom can do this but it removes all instances of a particular value. I just want the first one removed.
I'm not too experienced with standard library algorithms and I can't find the appropriate methods (if any) to do this. Here is an example:
int main() {
std::vector<int> v{1, 2, 3, 3, 4};
remove_first(v, 3);
std::cout << v; // 1, 2, 3, 4
}
So how would I go about removing the first occurance of a 3 from this vector?
Find it first, then erase it:
auto it = std::find(v.begin(),v.end(),3);
// check that there actually is a 3 in our vector
if (it != v.end()) {
v.erase(it);
}
If you don't care about maintaining the ordering of the elements in the vector, you can avoid the copy of the "tail" of remaining elements on erase:
auto it = std::find(v.begin(), v.end(), 3);
if (it != v.end()) {
std::iter_swap(it, v.end() - 1);
v.erase(v.end() - 1);
}

How do I remove the first occurrence of a value from a vector?

I'm trying to get a single element from a vector and push it to the back of the vector then remove it so I won't have an empty section in memory. The erase-remove idiom can do this but it removes all instances of a particular value. I just want the first one removed.
I'm not too experienced with standard library algorithms and I can't find the appropriate methods (if any) to do this. Here is an example:
int main() {
std::vector<int> v{1, 2, 3, 3, 4};
remove_first(v, 3);
std::cout << v; // 1, 2, 3, 4
}
So how would I go about removing the first occurance of a 3 from this vector?
Find it first, then erase it:
auto it = std::find(v.begin(),v.end(),3);
// check that there actually is a 3 in our vector
if (it != v.end()) {
v.erase(it);
}
If you don't care about maintaining the ordering of the elements in the vector, you can avoid the copy of the "tail" of remaining elements on erase:
auto it = std::find(v.begin(), v.end(), 3);
if (it != v.end()) {
std::iter_swap(it, v.end() - 1);
v.erase(v.end() - 1);
}

swap sequences of different length between two vectors in c++

Say you have two integer vectors:
I would like to define a function that allows me to swap a range of elements among the two vectors passing start index and lenght of the two sequences as arguments.
For instance: where and are vectors and the numbers passed as arguments represents starting index and lenght of the sequences.
In this case I should get as autput
v1 = 1,2, 13,14,15 ,5,6,7,8,9
v2 = 10,11,12, 3,4 ,16,17,18
the signature of the function I defined as an example is not a constraint, if you think there is a better way it is ok
It appears that all the regular STL algorithms fall short of what you want to do exactly:
std::swap_ranges would be almost there, but it requires that you swap equally long ranges
std::rotate would also not be bad, but it requires that the end point of one range equals the begin point of the second range.
// pseudo-splice on vector
v1.insert(v1.begin() + 2 + 2, v2.begin() + 3, v2.begin() + 3 + 3);
v2.erase(v2.begin() + 3, v2.begin() + 3 + 3);
// pseudo-splice on vector
v2.insert(v2.begin() + 3, v1.begin() + 2, v1.begin() + 2 + 2);
v1.erase(v1.begin() + 2, v1.begin() + 2 + 2);
You can of course easily abstract this into a function template that takes arbitrary iterator boundaries for your two ranges.
Edit based on David's comment, you can do some optimization to avoid needless resizing
// compute smallest range here, in this case it's the v1 part
std::swap_ranges(v1.begin() + 2, v1.begin() + 2 + 2, v2.begin() + 3);
// now handle the remaining part of the longest range, in this case it's element v2 + 3 + 2
std::insert(v1.begin() + 2 + 2, v2.begin() + 3 + 2);
std::erase(v2.begin() + 3 + 2);
Update: it would be easier if you used std::list since then you could use splice (I rearranged the insert / erase part there to mimic the code below)
v1.splice(v1.begin() + 2 + 2, v2, v2.begin() + 3, v2.begin() + 3 + 3);
v2.splice(v2.begin() + 3, v1, v1.begin() + 2, v1.begin() + 2 + 2);
I think this shouldn't raise any difficulties besides the fact that you will have to re-assign the vectors if Length1 != Length2.
swap_elements(v1, start1, length1, v2, start2, length2){
if(length1 != length2){
//alloc mem for both of the arrays
//copy the unmodified portions of the original arrays into the new arrays
}
//swap the elements
}
Doing it in place with insert and erase has worse running time than the code below because each time insert and erase are called the other elements of the vector have to be moved accordingly in memory, and allocate new memory block, move vector elements to new memory block, if necessary. To avoid resizing and memory issues, it might be preferable to just create two new vector at the very beginning.
swap_elements(vector<int> &v1, int s1, int l1, vector<int> &v2, int s2, int l2){
vector<int> nv1(v1.begin(),v1.begin()+s1);
vector<int> nv2(v2.begin(),v2.begin()+s2);
for(int i=0;i<l2;i++)
nv1.push_back(v2[s2+i]);
for(int i=0;i<l1;i++)
nv2.push_back(v1[s1+i]);
for(int i=s1+l1+1;i<v1.size();i++)
nv1.push_back(v1[i]);
for(int i=s2+l2+1;i<v2.size();i++)
nv2.push_back(v2[i]);
v1.clear();
v2.clear();
v1=nv1;
v2=nv2;
}