Some things to note :
- A you said there are
std::min(m,n)/2;
rings. - the ring
R(r, p)
has2*(r+p-2)
elements. - the top left of the ring has always coordinates
(x,x)
. This is visible in your code :)j = ring_i; v_ring.push_back(mat[ring_i][j]);
- Your code is using a relatively efficient amount of memory for the in-place transformation, and running in O(m*n) time, which is the best possible.
- A performance improvement will be to use a single vector for the data in the matrix such that
m(x,y) = m.data[x * cols + y]
Can we do it differently ?
Given the local coordinates system where (0, 0) is the top left of the ring
A coordinate (i,j), 0<=i<w, 0<=j<h, j == 0 || i == 0
on a side of a ring, can be moved by one position using the function
std::pair<int, int>
rotated_local_next(int i, int j, int h, int w)
{
if(j == 0) //top , special case top-right
{
return i == w-1 ? {i, ++j} : {++i, j};
}
if(i == w-1) //right, special case bottom-right
{
return j == h-1 ? {--i, j} : {i, ++j};
}
if(j == h-1) //bottom, special case bottom-left
{
return i == 0 ? {i, --j} : {--i, j};
}
if(i == 0) //left, special case top-left
{
return j == 0 ? {++i, j} : {i, --j};
}
// unreachable
}
You can now implement in matrix coordinates
std::pair<int, int>
rotated_matrix_next(int x, int y, int ring_i, int n, int m)
{
// remember ring_i is now
auto local_point = rotated_local_next(x-ring_i, y-ring_i, n-2*(ring_i+1), m-2*(ring_i+1))
return {local_point.first + ring_i, local_point.second + ring_i};
}
All you need to do now is to implement iterators satisfying MoveAssignable and MoveConstructible. The following is just pseudocode
class ring_iterator
{
public:
ptr_matrix p;
int x, int y, int ring;
// Forward
ring_iterator operator++() {
posnext = rotated_matrix_next(x, y, ring, p->size(), (*p)[0].size());
ring_iterator next_iter = *this;
next_iter.x = posnext.first;
next_iter.y = posnext.second;
}
// Swappable
void swap(ring_iterator& other) {
int& mine = (*p)[x][y];
int& theirs = other.(*p)[x][y];
int tmp = mine;
mine = theirs;
theirs = tmp;
}
// usually required
reference operator*() { return (*p)[x][y]; }
pointer operator->() { &((*p)[x][y]); }
};
Once begin(), end() is implemented your code will become
for(auto ring_i=0; ring_i<n_rings; ++ring_i){
RingWiew view(&m, m.size(), m[0].size(), ring_i);
int r_modulo = r % view.ringNumberOfElements();
std::rotate(view.begin(),std::advance(view.begin(), r_modulo),v_ring.end());
}