这是我参与8月更文挑战的第14天,活动详情查看:8月更文挑战
题目
给定一个 n × n 的二维矩阵 matrix 表示一个图像。请你将图像顺时针旋转 90 度。
你必须在 原地 旋转图像,这意味着你需要直接修改输入的二维矩阵。请不要 使用另一个矩阵来旋转图像。
从旋转矩阵出发
我们知道:
题目所求的90°旋转,事实上是
- 行转列,列转行
例如:
1 2 3
4 5 6
7 8 9
->
7 4 1
8 5 2
9 6 3
发现没有,我们的123跑到最后一列去了
抛弃题目所给限制,我们通过这一结论,可以很快地通过辅助数组解决这个问题:
public static void rotate(int[][] matrix){
int n = matrix.length;
int[][] tmp = new int[n][n];
for (int i = 0; i < n; ++i) {
for (int j = 0; j < n; ++j) {
//我们将每一行的值,写到每一列上,第i行变成第(n-i)列
tmp[j][n - i - 1] = matrix[i][j];
}
}
for (int i = 0; i < n; ++i) {
for (int j = 0; j < n; ++j) {
//直接赋值就可以了
matrix[i][j] = tmp[i][j];
}
}
}
但是这里有一个问题:
- 我们用到了辅助矩阵
那么就不满足题目的限制了,我们需要使用若干个常数以及换一种思路,来解决这个问题。
实际上我们已经找到了映射的规则:
-
行转列,列转行,也就是上面的:
- 第i行变成第(n-i)列
-
细细说来,就是:
- (i,j)->(j,n-i)
那么根据这个映射规则,我们换一种思路:
- 我们旋转一部分(通过映射实现),带动另外一部分转动,最后做到整体的旋转
对于循环的逻辑确定,我们通过以下的例子来确定:
1 2 3
4 5 6
7 8 9
因为我们只有 常数个数用于临时记录,那么我们最好是一次只替换一组数,如下:
1 2 3
4 5 6
7 8 9
因为它们在旋转之后,刚好各发生了一次移动,且刚好是下一个数:
7 2 1
4 5 6
9 8 3
而且根据这个规则,我们知道了: 在一行的循环中,我们只需要将这个过程进行到倒数第二个数就可以了。
同理,我们也能够知道:我们只需要进行一半的列循环,就可以了。
那么自然得出我们的解决方法:
public static void rotate(int[][] matrix) {
int tmp1,tmp2;
int col = matrix.length-1;
for (int i = 0; i < matrix.length/2; i++) {
for (int j = i; j < col-i; j++) {
tmp1 = matrix[i][j];
for (int k = 0; k < 4; k++) {
int m = j,n = col-i;
tmp2 = matrix[m][n];
matrix[m][n] = tmp1;
tmp1 = tmp2;
i = m;
j = n;
}
}
}
}