Skip to main content
1 of 4
UmNyobe
  • 219
  • 2
  • 11

Some things to note :

  • A you said there are std::min(m,n)/2; rings.
  • the ring R(r, p) has 2*(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

qt example of this coordinate

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());
}
UmNyobe
  • 219
  • 2
  • 11