我想使用递归来解决面试问题:
"给定由NxN矩阵表示的图像,其中图像中的每个像素是4个字节,写一个方法将图像旋转90度。你能这样做吗?"
我的想法是旋转" square"的最外面4个边。然后递归调用swap来旋转内部" square&#34 ;; 但是,我无法确定应该作为递归函数传入的第一个参数(我标记了它" ????",我应该填写什么?)
#include <iostream>
using namespace std;
const static int N = 4;
void swap(int** a, int length)
{
if (length <= 1)
{
return;
}
int top[length];
for (int i=0; i<length ; i++)
{
top[i] = a[0][i];
}
// left to top
for (int i=0; i < length; i++)
{
a[0][i] = a[length-i-1][0];
}
// bottom to left
for (int i=0; i < length; i++)
{
a[i][0] = a[length-1][i];
}
// right to bottom
for (int i=0; i < length; i++)
{
a[length-1][i] = a[length-i-1][length-1];
}
// top to right
for (int i=0; i < length; i++)
{
a[i][length-1]= top[i];
}
swap(????, length-2);
}
int main()
{
int a[N][N] = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12},
{13, 14, 15, 16}
};
int *b[N]; //surrogate
for (size_t i=0;i<N; i++)
{
b[i] = a[i];
}
swap(b, N);
return 0;
}
答案 0 :(得分:3)
这是我的努力。计划是我们的旋转可以分解成只旋转四个像素的组(通过考虑每次旋转方形90度时映射的位置,从一个像素组成的组)。
#include <iostream>
#include <iomanip>
template<typename PixelT, size_t N>
void rotate(PixelT (&pixel)[N][N], size_t shell = 0)
{
// end recursion condition
if ( shell >= N / 2 )
return;
// These variables will be optimized out, have written them here
// explicitly so it is clear what is going on. They represent the
// coordinate of those named sides of the square we are working on.
auto top = shell;
auto left = shell;
auto bottom = (N-1) - shell;
auto right = (N-1) - shell;
// For each pixel on the top side, rotate the four pixels
// it maps to under 90 degree rotation
for (auto i = 0; i < right - left; ++i)
{
// Anti-clockwise
auto tmp = pixel[top][left+i];
pixel[top][left+i] = pixel[top+i][right];
pixel[top+i][right] = pixel[bottom][right-i];
pixel[bottom][right-i] = pixel[bottom-i][left];
pixel[bottom-i][left] = tmp;
}
// Rotate next shell in
rotate(pixel, shell + 1);
}
template<typename PixelT, size_t N>
void dump(PixelT (&pixel)[N][N])
{
std::cout << "[\n";
for (auto&& row : pixel)
{
for (auto&& pix : row)
std::cout << std::setw(4) << pix;
std::cout << '\n';
}
std::cout << "]\n";
}
int main()
{
uint32_t a[4][4] = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12},
{13, 14, 15, 16}
};
dump(a);
rotate(a);
dump(a);
}
递归的使用有点似是而非,因为递归参数可以被一行for
循环替换。
类似地,我实际使用的foor
循环也可以用递归替换(所以我们为每个四像素集递归一次)
答案 1 :(得分:1)
有很多方法可以做到这一点,并非所有方法都是直截了当的,并且通常,在C ++中使用std::vector
或std::array
会使任务变得更加容易。而且我认为你可以在没有递归的情况下做到这一点,只需要调用swap
来完成嵌套for
- 循环的工作。
请参阅:How to rotate a N x N matrix by 90 degrees?或下面的M.M's answer。
但这是我为使你的方法发挥作用所做的:
#include <iostream>
using namespace std;
const static int N = 4;
void swap(int** a, int length)
{
if(length <= 1)
{
return;
}
{
std::vector<int> top(length); // variable-length arrays not allowed in C++
for(int i = 0; i<length; i++)
{
top[i] = a[0][i];
}
// left to top
for(int i = 0; i < length; i++)
{
a[0][i] = a[length - i - 1][0];
}
// bottom to left
for(int i = 0; i < length; i++)
{
a[i][0] = a[length - 1][i];
}
// right to bottom
for(int i = 0; i < length; i++)
{
a[length - 1][i] = a[length - i - 1][length - 1];
}
// top to right
for(int i = 0; i < length; i++)
{
a[i][length - 1] = top[i];
}
}
std::vector<int*> b(length - 2); // creating another surrogate array
for(int i = 0; i < length - 2; i++)
b[i] = a[i + 1] + 1; // making it represent the inner square
swap(b.data(), length - 2);
}
int main()
{
int a[N][N] = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12},
{13, 14, 15, 16}
};
int *b[N]; //surrogate
for(size_t i = 0; i<N; i++)
{
b[i] = a[i];
}
swap(b, N);
for(int i = 0; i < N; i++)
{
for(int j = 0; j < N; j++)
cout << a[i][j] << " ";
cout << endl;
}
return 0;
}
答案 2 :(得分:0)
2015年12月12日 - 更新 - 见下文:9x9的“开发输出”
这是一个递归方法的示例。
我会说这是'不完全'就地,但接近。我选择将外围值捕捉到矢量,然后将它们踩到新的旋转位置。虽然这可以使用更多std :: swap()来实现,但我发现代码令人反感。最大矢量(外周长)的大小小于2d阵列(即4x4的12对16个元素)。我想std :: swap()也使用单个元素临时存储。所以,不是就地。
我使用std :: array()作为2d数组。这鼓励我在开发期间使用其他模板工具(std :: rotate(),range-for和其他一些)。最后,我选择了一种方法,我认为更符合您的问题陈述。唉,它没有利用它们......哦,好吧。
我有一个嵌套类。 Corner_t标识左上角或右下角。这应该定义周长,如“void recursiveRotate2d90(Corner_t&amp;,Corner_t&amp;)”所使用的那样,
#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);
}
输出:
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
--------------
以前是,相当无聊,看起来很容易。请注意,当我提交时,我删除了所有开发和诊断cout,因此没有提示中间操作或递归。
以下是我的代码的下一个版本(未包含)的输出,并演示了启用了开发cout的数独大小(9x9)2d矩阵的旋转!此外,此版本首先递归到最小的框,然后执行另一个框以相反的顺序旋转(最小的框到最大框,总共4个中间框)。
每个中间数组都会突出显示已旋转的“框”,因此您可以看到“进度”,即运算中的算法。
留意点......
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
--------------
答案 3 :(得分:0)
您可以在不为其他矩阵或行分配空间的情况下执行此转换,但仍需要至少一个临时变量来执行以下操作:
// rotate the corners of a 4x4 2D array:
temp = a[0][0];
a[0][0] = a[0][3];
a[0][3] = a[3][3];
a[3][3] = a[3][0];
a[3][0] = temp;
因此,您只需要从矩阵的子集开始迭代前一个过程,这要归功于旋转模拟法。例如,考虑5x5和4x4矩阵:并用*表示起始单元格:
* * * * o * * * o * are the starting cells of the cyclical swap
o * * o o o * o o
o o o o o o o o o
o o o o o o o o o
o o o o o
为了概括公式,您可以将此视为矩阵元素在其中间居中的旋转。因此,对于矩阵NxN,我们有:
使用这种循环交换策略旋转2D数组的代码不需要递归,嵌套循环就足够了。
void rotate_counterclockwise( int ** a, size_t nCols ) {
size_t nn = nCols - 1;
size_t hh = nCols / 2;
for ( size_t i = 0; i < hh; i++ ) {
for ( size_t j = i; j < (nn - i); j++) {
int temp = a[i][j]; // top left
a[i][j] = a[j][nn-i]; // top right
a[j][nn-i] = a[nn-i][nn-j]; // bottom right
a[nn-i][nn-j] = a[nn-j][i]; // bottom left
a[nn-j][i] = temp;
}
}
}
如果你想进行顺时针旋转,你只需要在前面的代码中将右上角元素与左下角元素交换,或者将它实现为交换为lambdas:
#include <functional>
auto ccw_swap = [] (int & v1, int & v2, int & v3, int & v4 ) {
int temp = v1;
v1 = v2;
v2 = v3;
v3 = v4;
v4 = temp;
};
auto cw_swap = [] (int & v1, int & v2, int & v3, int & v4 ) {
int temp = v1;
v1 = v4;
v4 = v3;
v3 = v2;
v2 = temp;
};
auto ud_swap = [] (int & v1, int & v2, int & v3, int & v4 ) {
int temp = v1;
v1 = v3;
v3 = temp;
temp = v2;
v2 = v4;
v4 = temp;
};
void rotate_loop( int ** a, size_t nCols, std::function< void (int & v1, int & v2, int & v3, int & v4 ) > inner_loop ) {
size_t nn = nCols - 1;
size_t hh = nCols / 2;
for ( size_t i = 0; i < hh; i++ ) {
for ( size_t j = i; j < (nn - i); j++) {
inner_loop( a[i][j], a[nn-j][i], a[nn-i][nn-j], a[j][nn-i]);
}
}
}
请注意,对于180°旋转,由于特定的旋转模拟,该方案的效果较差。对于换位和水平或垂直镜像,该方案是不适用的。
作为进一步的概括,我建议考虑将矩阵存储在单个数组中(或更好的std::vector
)。
void rotate_loop( int * a, size_t nCols, std::function< void (int & v1, int & v2, int & v3, int & v4 ) > inner_loop ) {
size_t nn = nCols - 1;
size_t n1 = nCols * nn;
size_t n2 = nCols * nCols - 1;
size_t hh = nCols / 2;
for ( size_t i = 0, ni = 0; i < hh; i++ ) {
for ( size_t j = i, nj = ni, nij = ni + j, nji = nj - i; j < (nn - i); j++) {
inner_loop( a[nij], // = a[i + i*N + (j - i)] = a[i*N + j]
a[nn+nji], // = a[N - 1 - i + i*N + (j-i)*N] = a[N-1-i+j*N]
a[n2-nij], // = a[N*N - 1 - i - i*N - (j-i)] = a[N*N-1-i*N-j]
a[n1-nji]); // = a[N*(N-1) + i - i*N - (j-i)*N] = a[N*(N-1)+i-j*N]
nj += nCols; // nj = j*N
nij++; // nij = i*N + j
nji += nCols; // nji = j*N - i
}
ni += nCols; // ni = i*N
}
}
enum direction { counterclockwise, clockwise, upside_down };
template < typename T >
void rotate( T a, size_t nCols, direction angle = counterclockwise ){
if ( !a ) return;
switch ( angle ) {
case counterclockwise:
rotate_loop(a,nCols,ccw_swap);
break;
case clockwise:
rotate_loop(a,nCols,cw_swap);
break;
case upside_down:
rotate_loop(a,nCols,ud_swap);
break;
}
}
void show( int * a, size_t nCols ) {
for(int i = 0; i < nCols; i++) {
for(int j = 0; j < nCols; j++)
std::cout << " " << *(a++);
std::cout << std::endl;
}
}