是一个面试题,我想了好久,果然好久不做类似算法题的话会思维迟钝。题目还有一个条件是每个点用4 bytes存储,实际上是每个点是int类型的暗示,这个矩阵就是int[][]的结构。
我是先考虑点的旋转规律的,用了一点三角函数和计算机图形学的坐标变换,实际上归纳法也是可以的,只是我没找出规律。之后是实际编码,比想象中要麻烦一些,和参考答案也是有点不同的。
首先说下变化规律,计算机坐标系下(x, y)点顺时针旋转90度后结果是(y, n – x),通过多次数据归纳也可以做,以下是图形学上的证明:
--------------------> | (x, y) | | | (y, n - x) | v
计算机坐标系是左上角为原点,向右扩展x,向下扩展y。普通坐标系是
^ | A(x, y) | B(?, ?) \ | / --------------------------> | | |
如果放在普通坐标系下,B的坐标是(Rsina, Rcosa),R是半径,a是B-原点和x轴的夹角。假设A顺时针旋转90度到B,那么A点坐标是(Rsin(a+90), Rcos(a+90)。这里要用到初中学的三角函数了,可惜我忘得差不多了。查了下结果是
sin(a+90) = sina cos90 + cosa sin90 = cosa
cos(a+90) = cosa cos90 – sina sin90 = -sina
换句话说(Rsin(a+90), Rcos(a+90)) = (Rcosa, -Rsina),如果原先(Rsina, Rcosa)定义为(x, y)的话,那么A点坐标为(y, -x)。
这里论证的是常规坐标下,实际要从计算机坐标系->常规坐标系->计算机坐标系。这里的转换其实没那么难,就是坐标系的平移(加减delta)和镜像(正负号)。举个具体例子,原先计算机坐标系为(a, b)的点,变成常规坐标系后坐标为(a – n / 2, -(b – n / 2)),这里包括两个操作:平移和镜像。
现在我们按照上面旋转90度的公式和坐标变换的公式可以得到:
计算机坐标系 | 常规坐标系 | |
A(变换后的点) | 1 (x, y) | 2 (x – n / 2, n / 2 – y) |
B(原始点) | 4 (y, n – x) | 3 (y – n / 2, x – n /2) |
注意,这里按照1,2,3,4的方式看的话容易理解。另外这里论证的是目标点是(x, y),原始点的位置,如果你逆向推理,原始点是(x, y),那么目标点也恰好是(y, n – x)。
论证了变换规律之后,实际编码还有另外一个问题:你不能按照二维扫描的方式旋转,原题其实还有个条件是in place即就地旋转。其实这块我没仔细考虑,在阅读了参考答案之后明白还有这么一茬。参考答案中给的方法是类似蛋卷的方式,先变换周围一圈,再内圈。变换圈的时候4条边轮换,(y, n – x)的点被变换到(x, y)上。这里其实还有一个细节,是int[][] matrix下matrix[a][b]对应是(b, a)不是(a, b),所以要变换matrix[n – x][y]到matrix[y][x]。同时还有一个数组是从零开始计数的,所以这里n实际上要减去1,所以最终的代码如下:
private static void rotate(int[][] matrix, int n) { for (int layer = 0; layer < n / 2; layer++) { for (int x = layer; x < n - layer - 1; x++) { int value = matrix[layer][x]; matrix[layer][x] = matrix[n - x - 1][layer]; matrix[n - x - 1][layer] = matrix[n - layer - 1][n - x - 1]; matrix[n - layer - 1][n - x - 1] = matrix[x][n - layer - 1]; matrix[x][n - layer - 1] = value; } } }
同时附上参考答案:
public static void rotate(int[][] matrix, int n) { for (int layer = 0; layer < n / 2; ++layer) { int first = layer; int last = n - 1 - layer; for(int i = first; i < last; ++i) { int offset = i - first; int top = matrix[first][i]; // save top // left -> top matrix[first][i] = matrix[last-offset][first]; // bottom -> left matrix[last-offset][first] = matrix[last][last - offset]; // right -> bottom matrix[last][last - offset] = matrix[i][last]; } } }