Reputation: 1141
I know there is std::swap
that can swap two element in a vector, or iteratively swap two segments with the same length. For example, I can write a loop to swap bcd
and efg
in the vector abcdefgh
, resulting in aefgbcdh
. But can I do swap bcd
and ef
(different length)? Or is there any other function in std::vector
that can achieve this?
Upvotes: 3
Views: 1849
Reputation: 21
void swap(std::vector<int>& v, int start1, int end1, int start2, int end2)
{
int size2 = end2 - start2 + 1;
int size1 = end1 - start1 + 1;
auto begin = v.begin() + start1;
auto end = v.begin() + end2 + 1;
std::rotate(begin, v.begin() + start2, end);
std::rotate(begin + size2, begin + size2 + size1, end);
}
This is a generic version to do the work using double rotate trick :)
Upvotes: 2
Reputation: 275500
This reduces to an equal length interval swap followed by a rotate.
The equal length interval swap reduces the problem to 'move an interval to another spot', which can be implemented in-place with std::rotate
.
If the half open interval [a,b)
is moving forward to x
, then:
std::rotate( a,b,x );
if moving backward to y
then:
std::rotate( y,a,b );
If we assume that we can get the intervals in the correct order (left interval before the right in the sequence), then we can do this:
template<class I>
void swap(I a, I b, I x, I y){
using std::distance; using std::next; using std::advance;
auto d_ab=distance(a,b);
auto d_xy=distance(x,y)
auto d=(std::min)(d_ab,d_xy);
std::swap_ranges(a,next(a,d),x);
advance(a,d);
advance(x,d);
if (a==b){
std::rotate(b,x,y);
}else{
std::rotate(a,b,x);
}
}
micro-optimizations that avoid going over elements more than once can be done, but for vector iterators the extra advances shoukd be next to free.
An industrial strength one would do tag dispatching, write a slick one for random access iterators, and for other iterators do a double iterator loop swap elements (manual swap ranges that bounds checks), with a similar rotate
at the end.
If we do not know that the order is a
, b
, x
, y
we need to require random access iterators (and gain access to <
). Then we need to wrap the std::rotate
calls above to change the order of the arguments.
With access to an end
iterator, we can do it inefficiently with forward iterators still.
Upvotes: 3
Reputation: 103713
If the segments are adjacent, you can use std::rotate
. For example:
std::vector<int> v = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
Let's say I want to swap the { 1, 2, 3 }
with the { 4, 5, 6, 7 }
. I can do this:
std::rotate(v.begin() + 1, v.begin() + 4, v.begin() + 8);
If the segments are not adjacent, you can do it by using rotate
twice, but it may do more work than is strictly necessary. For example, to swap { 1, 2 }
with { 4, 5, 6, 7 }
std::rotate(v.begin() + 1, v.begin() + 4, v.begin() + 8);
std::rotate(v.begin() + 5, v.begin() + 7, v.begin() + 8);
Upvotes: 6