Which is the smartest way to add at the beggining of a vector the last two elements of the vector itself and add at the end of the vetor the first two element of the vector?
I mean, if my starting vector is
v = 1 2 3 4 5 6 7 8 9
I need that it becames
v = 8 9 1 2 3 4 5 6 7 8 9 1 2
First, if the container is going to be large then consider using deque instead of vector. It's more efficient for adding at the start.
For vector you can't insert elements out of the vector to the start because the first thing that happens is everything in the vector gets moved (and all iterators and references to those elements are invalidated). So you either need to copy the elements out of the vector or you need to put insert elements at the start and then copy-assign to them. Assuming the type is int, I'll go with the former:
if (v.size() >= 2) {
int tmp[] = {*(v.end() - 2), *(v.end() - 1)};
v.insert(v.begin(), tmp, tmp + 2);
tmp[0] = v[2]; tmp[1] = v[3];
v.insert(v.end(), tmp, tmp + 2);
}
Alternatively, this uses more memory but might be easier to read. As a bonus it gives the strong exception guarantee even with types whose copy constructor can throw. My code above can be made to offer the strong guarantee by adding a call to reserve, but only because int is a trivial type:
if (v.size() >= 2) {
std::vector<int> new_v;
new_v.reserve(v.size() + 4);
new_v.insert(new_v.end(), v.end() - 2, v.end());
new_v.insert(new_v.end(), v.begin(), v.end());
new_v.insert(new_v.end(), v.begin(), v.begin() + 2);
v.swap(new_v);
}
For deque you don't need to store any elements outside the container provided you use a reference instead of an iterator to access them. Again this only offers the basic exception guarantee.
if (v.size() >= 2) {
v.push_front(v.back());
v.push_front(*&(v.end() - 1));
v.push_back(*&(v.begin() + 2));
v.push_back(*&(v.begin() + 3));
}
My five cents
std::vector<int> v = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
v.insert( v.end(), { v[0], v[1], v[v.size() - 2], v[v.size() - 1] } );
std::rotate( v.begin(), std::prev( v.end(), 2 ), v.end() );
for ( int x : v ) std::cout << x << ' ';
std::cout << std::endl;
By using the vector::insert function for ranges. It is easier to first insert the first two elements at the end.
v.insert(v.end(), v.begin(), v.begin()+2);
v.insert(v.begin(), v.end()-2, v.end());
Edit:
Is undefined behaviour.
Appending std::vector to itself, undefined behavior?
Related
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.
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);
}
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);
}
Having this vector
vector<int> v{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
how can I compute the sum of the first half of it (which is 15) using the accumulate function?
Can I do this using the for loop with only the iterators (not numerical indexes)?
You can
accumulate(v.begin(), v.begin()+int(v.size()/2), 0)
if v is your vector.
You can also write a loop:
int sum = 0;
for (vector<int>::iterator it = v.begin(); it != v.begin+int(v.size()/2); ++it) {
sum += *it;
}
To work with just the first half you have to get iterators that cover just that range. Usually people want to work with an entire container and so they use begin and end functions, but that's not the only way:
auto begin = std::begin(v);
auto middle = std::begin(v) + v.size()/2; // works for random access iterators
auto middle = begin;
std::advance(middle, v.size()/2);
advance works for input iterators or better, but for input iterators which are not also one of the other types the items that are advanced passed won't be accessible anymore.
auto middle = std::next(begin, v.size()/2); // C++11. works for forward iterators
And these are only a few of the available operations you can perform on the different types of iterators.
So now that you can create iterators that specify your desired range you can use them either in std::accumulate or a manual for loop:
std::accumulate(std::begin(v), std::next(std::begin(v), v.size()/2), 0);
for (auto begin(std::begin(v)), end(begin+v.size()/2); begin!=end; ++begin) {
...
}
accumulate<int>(v.cbegin(), v.cbegin() + v.size()/2, 0);
int sum = std::accumulate(v.begin(), v.begin() + v.size() / 2, 0);
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;
}