12/12/2015 - UPDATE - see below: "development output" of 9x9
Here is an example of one recursive approach.
I would say this is 'not quite' in-place, but close. I chose to capture to a vector the outer perimeter values, then stomp them into their new rotated locations. Though this can be accomplished using a bunch more std::swap(), I found that code distasteful. The size of the largest vector (for outer perimeter) is smaller than the 2d array (i.e. 12 vs 16 elements for 4x4). I suppose std::swap() also uses a single element temp storage. So, not quite in-place.
I used std::array() for the 2d array. This encourage me to use other template tools during development (std::rotate(), range-for and a few others). In the end I chose an approach I think conforms more to your problem statement. Alas, it did not make use of them ... oh well.
I have 1 nested class. A Corner_t identifies either the top left or bottom right corners. This is enought to define the perimeter, as used by "void recursiveRotate2d90(Corner_t&, Corner_t&)",
#include <iomanip> // std::setw()
// Note: <iomanip> pulled in std::array,
// but because your tools might not:
#include <array> // std::array
#include <iostream> // std::cout, std::endl
#include <vector> // std::vector
class Arr2d_t
{
// forward declaration (nested class)
class Corner_t;
public:
static const int N = 4; // can make N into template parameter
// needs different initialization
Arr2d_t(void)
{
init();
}
~Arr2d_t(void) {
// default std::array dtor does what is needed for cleanup
};
int nGet() { return (N); }
int exec(void)
{
const std::string HDRLINE (" --------------");
// class ctor initialized "std::array <std::array <int, N>, N> m_2d"
std::cout << "\nas initialized by ctor: \n" << HDRLINE << std::endl;
show();
std::cout << HDRLINE << std::endl;
// recursive, ~in-place, rotate starting at outer perimeter
Corner_t ulc(0, 0, this, "ulc");
Corner_t lrc(N-1, N-1, this, "lrc");
std::cout << "\nrecursively rotate 2d array by 90 (clockwise) : " << std::flush;
recursiveRotate2d90(ulc, lrc);
std::cout << "\n" << HDRLINE << std::endl;
show();
std::cout << HDRLINE << std::endl;
return (0);
}
private:
void show()
{
for(int r = 0; r < N; r++)
{
for(int c = 0; c < N; c++)
std::cout << std::setw(3) << m_2d[r][c] << " ";
std::cout << std::endl;
}
}
void init()
{
int n = 1;
for (size_t r=0; r<N; ++r)
for (size_t c=0; c<N; ++c)
m_2d[r][c] = n++;
}
// recursively rotate 2d array by 90 degrees
void recursiveRotate2d90(Corner_t& ulCnr, // Top Left Corner row,col
Corner_t& lrCnr) // Bottom Right Corner row,col
{
int topRow = ulCnr.top();
int leftCol = ulCnr.left();
int botRow = lrCnr.bot();
int rightCol = lrCnr.right();
int sz = botRow - topRow; // 4x4: 3 - 0 => 3 or internal 2x2: 2 - 1 = 1
if (sz < 1) return; // recursion exit
{
// build a simple list of the 'outer perimeter' elements
std::vector<int> perimeterValues; // capture m_2d perimeter values
// capture 2d array perimeter values
{
// top row
for(int i = 0; i < sz; ++i) {
perimeterValues.push_back ( m_2d [topRow] [leftCol+i] ); // topRow ++
}
// right col
for(int i = 0; i < sz; ++i)
{
perimeterValues.push_back ( m_2d [topRow+i] [rightCol]); // rightCol ++
}
// bottom row
for(int i = 0; i < sz; ++i) {
perimeterValues.push_back ( m_2d [botRow] [rightCol-i]); // botRow --
}
// left col reverse
for(int i = 0; i < sz; ++i) {
perimeterValues.push_back( m_2d [botRow-i] [leftCol]); // leftCol --
}
}
// overwrite the captured perimeter values to achieve the rotation desired
{
int nxtOut = 0;
// to right col - from top row
for(int i = 0; i < sz; ++i) m_2d [topRow+i] [rightCol] = perimeterValues[nxtOut++];
// to bottom row - from right col
for(int i = 0; i < sz; ++i) m_2d [botRow] [rightCol-i] = perimeterValues[nxtOut++];
// to left col - from bottom row
for(int i = 0; i < sz; ++i) m_2d [botRow-i] [leftCol] = perimeterValues[nxtOut++];
// to top row - from left column
for(int i = 0; i < sz; ++i) m_2d [topRow] [leftCol+i] = perimeterValues[nxtOut++];
//
}
} // dtor perimeterValues
// repeat against next inner perimeter
Corner_t ulc = ulCnr.rightDown();
Corner_t brc = lrCnr.leftUp();
recursiveRotate2d90 (ulc, brc); // recurse
} // void recursiveRotate2d90(Corner_t&, Corner_t& )
private: // data attributes
std::array <std::array <int, N>, N> m_2d;
void showVec1(std::vector<int> v)
{
std::cout << "\n";
for (size_t i=0; i<v.size(); ++i)
if(v[i]) std::cout << " " << v[i];
else std::cout << "?" << i;
std::cout << std::endl;
show();
}
void showVec2(std::vector<int> v2)
{
for (size_t i=0; i<v2.size(); ++i)
if(v2[i]) std::cout << " " << std::setw(2) << v2[i] << " ";
else std::cout << "?" << std::setw(2) << i;
std::cout << std::endl;
}
// //////////////////////////////////////////////////////////////////////
class Corner_t
{
public:
// vvvvvvvvvvvvvv -- class instance to access
Corner_t (int r, int c, Arr2d_t* arr2d, const std::string& lbl) :
m_r (r),
m_c (c),
m_a2d(arr2d),
m_lbl(lbl)
{
}
~Corner_t() { }
inline int r() { return (m_r); }
inline int c() { return (m_c); }
inline int left() { return (m_c); }
inline int right() { return (m_c); }
inline int top() { return (m_r); }
inline int bot() { return (m_r); }
// tlc
Corner_t rightDown() {
Corner_t retVal(m_r+1, m_c+1, m_a2d, (m_lbl+'1'));
return retVal;
}
// brc
Corner_t leftUp () {
Corner_t retVal(m_r-1, m_c-1, m_a2d, (m_lbl+'2'));
return retVal;
}
void showCrnr(char k = '-') {
std::cout << m_lbl << k << " Corner_t: " << this << " "
<< m_r << ", " << m_c << std::endl;
}
private:
int m_r;
int m_c;
Arr2d_t* m_a2d; // capture pointer to class
std::string m_lbl;
}; // class Corner_t
}; // class Arr2d_t
// /////////////////////////////////////////////////////////////////////////
int t270(void)
{
std::cout << "\nsizeof(Arr2d_t): " << sizeof(Arr2d_t) << std::endl;
Arr2d_t arr2d;
int N = arr2d.nGet();
std::cout << "class Arr2d_t has only 1 data attribute: "
<< "\n\n std::array <std::array <int, N>, N> m_2d; "
<< "\n\nwhere N is " << N << ". instance sizeof(arr2d): "
<< sizeof(arr2d) << std::endl;
std::cout << "\n" << N << "x" << N << " * sizeof(int) "
<< "= " << N*N << " * " << sizeof(int)
<< " = " << N*N*sizeof(int) << "\n\n" << std::endl;
int retVal = arr2d.exec();
return (retVal);
}
Output:
sizeof(Arr2d_t): 64
class Arr2d_t has only 1 data attribute:
std::array <std::array <int, N>, N> m_2d;
where N is 4. instance sizeof(arr2d): 64
4x4 * sizeof(int) = 16 * 4 = 64
as initialized by ctor:
--------------
1 2 3 4
5 6 7 8
9 10 11 12
13 14 15 16
--------------
recursively rotate 2d array by 90 (clockwise) :
--------------
13 9 5 1
14 10 6 2
15 11 7 3
16 12 8 4
--------------
The previous is, well, rather boring, and looks deceptively easy. Note that when I submitted I removed all development and diagnostic cout, so there is no hint of the intermediate actions or the recursion.
What follows is the output from the next version of my code (not included), and demonstrates the rotate of a sudoku size (9x9) 2d matrix with development cout's enabled! Furthermore, this version recurses to the smallest box first, and then performs the other box rotates in reverse order (smallest box to biggest, 4 intermediate boxes in all).
And each intermediate array highlights the 'box' that was rotated, so you get to see 'progress', the algorithms in action.
Watch for the dots...
as initialized by ctor via init():
--------------
1 2 3 4 5 6 7 8 9
10 11 12 13 14 15 16 17 18
19 20 21 22 23 24 25 26 27
28 29 30 31 32 33 34 35 36
37 38 39 40 41 42 43 44 45
46 47 48 49 50 51 52 53 54
55 56 57 58 59 60 61 62 63
64 65 66 67 68 69 70 71 72
73 74 75 76 77 78 79 80 81
--------------
recursively rotate 2d array by 90 (clockwise) :
recursion exit sz: 0
sz: 3 ul 3,3, lr 5,5
perimeter values:
31 32 33 42 51 50 49 40
1 2 3 4 5 6 7 8 9
10 11 12 13 14 15 16 17 18
19 20 21 22 23 24 25 26 27
28 29 30 49. 40. 31. 34 35 36
37 38 39 50. 41 32. 43 44 45
46 47 48 51. 42. 33. 52 53 54
55 56 57 58 59 60 61 62 63
64 65 66 67 68 69 70 71 72
73 74 75 76 77 78 79 80 81
sz: 5 ul 2,2, lr 6,6
perimeter values:
21 22 23 24 25 34 43 52 61 60 59 58 57 48 39 30
1 2 3 4 5 6 7 8 9
10 11 12 13 14 15 16 17 18
19 20 57. 48. 39. 30. 21. 26 27
28 29 58. 49 40 31 22. 35 36
37 38 59. 50 41 32 23. 44 45
46 47 60. 51 42 33 24. 53 54
55 56 61. 52. 43. 34. 25. 62 63
64 65 66 67 68 69 70 71 72
73 74 75 76 77 78 79 80 81
sz: 7 ul 1,1, lr 7,7
perimeter values:
11 12 13 14 15 16 17 26 35 44 53 62 71 70 69 68 67 66 65 56 47 38 29 20
1 2 3 4 5 6 7 8 9
10 65. 56. 47. 38. 29. 20. 11. 18
19 66. 57 48 39 30 21 12. 27
28 67. 58 49 40 31 22 13. 36
37 68. 59 50 41 32 23 14. 45
46 69. 60 51 42 33 24 15. 54
55 70. 61 52 43 34 25 16. 63
64 71. 62. 53. 44. 35. 26. 17. 72
73 74 75 76 77 78 79 80 81
sz: 9 ul 0,0, lr 8,8
perimeter values:
1 2 3 4 5 6 7 8 9 18 27 36 45 54 63 72 81 80 79 78 77 76 75 74 73 64 55 46 37 28 19 10
73. 64. 55. 46. 37. 28. 19. 10. 1.
74. 65 56 47 38 29 20 11 2.
75. 66 57 48 39 30 21 12 3.
76. 67 58 49 40 31 22 13 4.
77. 68 59 50 41 32 23 14 5.
78. 69 60 51 42 33 24 15 6.
79. 70 61 52 43 34 25 16 7.
80. 71 62 53 44 35 26 17 8.
81. 72. 63. 54. 45. 36. 27. 18. 9.
--------------
73 64 55 46 37 28 19 10 1
74 65 56 47 38 29 20 11 2
75 66 57 48 39 30 21 12 3
76 67 58 49 40 31 22 13 4
77 68 59 50 41 32 23 14 5
78 69 60 51 42 33 24 15 6
79 70 61 52 43 34 25 16 7
80 71 62 53 44 35 26 17 8
81 72 63 54 45 36 27 18 9
--------------