48.旋转图像




48. 旋转图像
二哥瞎逼逼:9 月份对于准备秋招的球友来说,非常重要,但奇怪的是 9 月份的
假期却很多,中秋过完马上就是国庆,就很容易刚提起劲然后就松懈下来,然后
一眨眼就 10 月中旬了。国庆结束后,基本上 offer 就开奖了,也就意味着头部
和肩部选手的秋招可以彻底告一个段落了。今天继续来一到 LeetCode 题,醒醒
脑子。
题意
给定一个 n × n 的二维矩阵 matrix 表示一个图像。请你将图像顺时针旋转 90 度。
你必须在 原地 旋转图像,这意味着你需要直接修改输入的二维矩阵。请不要 使用另一个
矩阵来旋转图像。
难度
中等
示例
示例 1:
048.旋转图像-20240918153439.png
输入:matrix = [[1,2,3],[4,5,6],[7,8,9]]
输出:[[7,4,1],[8,5,2],[9,6,3]]
示例 2:
048.旋转图像-20240918153507.png
输入:matrix = [[5,1,9,11],[2,4,8,10],[13,3,6,7],[15,14,12,16]]
输出:[[15,13,2,5],[14,3,4,1],[12,6,8,9],[16,7,10,11]]
分析 1
在我们上学的年级,经常出现一些找规律的题目,比如说 1,1,2,3,5,8,13,21 这样的数列,
我们很容易找到这样一个规律,就是后一个数等于前两个数之和。
对于这道题,我们也可以找到这样一个规律:
• 第 i 行的元素旋转到了第 n-1-i 列;
• 第 j 列的元素旋转到了第 j 行。
048.旋转图像-20240918154554.png
一旦找到这个规律,题解就很容易了,我们只需要遍历一遍矩阵,然后将矩阵的元素旋转
到对应的位置即可。
为了保证旋转的过程中有些元素不会被覆盖,我们可以对矩阵进行深拷贝。
题解如下:
class Solution04801 {
public void rotate(int[][] matrix) {
int n = matrix.length; // 获取矩阵的维度
int[][] matrix_new = new int[n][n]; // 创建一个新矩阵来保存原矩阵的副本
// 复制原矩阵的每一行到新的矩阵
for (int i = 0; i < n; i++) {
matrix_new[i] = Arrays.copyOf(matrix[i], n); // 复制每一行
}
// 旋转矩阵:将 matrix_new 中的元素重新放入 matrix 中
for (int i = 0; i < n; i++) { // 遍历原矩阵的每一行
for (int j = 0; j < n; j++) { // 遍历原矩阵的每一列
// 将 matrix_new[i][j] 旋转到 matrix[j][n - 1 - i]
matrix[j][n - 1 - i] = matrix_new[i][j];
}
}
}
public static void main(String[] args) {
Solution04801 solution = new Solution04801();
int[][] matrix = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};
solution.rotate(matrix);
for (int[] row : matrix) {
for (int val : row) {
System.out.print(val + " ");
}
System.out.println();
}
// 输出:
// 7 4 1
// 8 5 2
// 9 6 3
}
}
来看一下题解效率:
048.旋转图像-20240918160613.png
很高,但是因为我们多了一个 matrix_new 的二维数组,所以在严格意义上并不符合原地
旋转的要求。
分析 2
矩阵旋转 90 度,相当于把 A 挪到 B,B 挪到 C,C 挪到 D,D 挪到 A,这样的操作。
048.旋转图像-20240918161300.png
比如说一个 4x4 的矩阵,相当于是 2 层的同心圆,我们可以通过一层一层的旋转来完成
整个矩阵的旋转。
class Solution04802 {
public void rotate(int[][] matrix) {
int n = matrix.length;
// 逐层旋转
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];
// 左边 -> 上边
matrix[first][i] = matrix[last - offset][first];
// 下边 -> 左边
matrix[last - offset][first] = matrix[last][last - offset];
// 右边 -> 下边
matrix[last][last - offset] = matrix[i][last];
// 上边 -> 右边
matrix[i][last] = top;
}
}
}
public static void main(String[] args) {
Solution04802 solution = new Solution04802();
int[][] matrix = {
{ 1, 2, 3, 4},
{ 5, 6, 7, 8},
{ 9, 10, 11, 12},
{13, 14, 15, 16}
};
solution.rotate(matrix);
for (int[] row : matrix) {
for (int val : row) {
System.out.print(val + " ");
}
System.out.println();
}
// 输出:
// 13 9 5 1
// 14 10 6 2
// 15 11 7 3
// 16 12 8 4
}
}
题解效率也很高。
048.旋转图像-20240918162156.png
分析 3
旋转 90 度其实,就相当于先沿着对角线翻转,再沿着中轴线翻转。
048.旋转图像-20240918162855.png
也就是说:
①、对角线反转:将矩阵的行和列进行互换。即 matrix[i][j] 变为 matrix[j][i]。
②、中轴线反转:将翻转后的矩阵的每一行进行左右翻转。这样就可以得到旋转 90 度的
结果。
class Solution {
public void rotate(int[][] matrix) {
int n = matrix.length;
// 1. 对角线翻转
for (int i = 0; i < n; i++) {
for (int j = i; j < n; j++) {
int temp = matrix[i][j];
matrix[i][j] = matrix[j][i];
matrix[j][i] = temp;
}
}
// 2. 中轴线翻转
for (int i = 0; i < n; i++) {
for (int j = 0; j < n / 2; j++) {
int temp = matrix[i][j];
matrix[i][j] = matrix[i][n - 1 - j];
matrix[i][n - 1 - j] = temp;
}
}
}
}
来看一下题解效率:
048.旋转图像-20240918163629.png
总结
?道的关
题的于
住关 在
就
到
不
,
系
关
前
之
和
素
元
的
后
旋
:
律
找 键在 于 找 规律 : “旋 转后 的 元 素 和 旋 转之 前 的 关 系 ”, 找 不 到 就 记住 ? :
• 第一种解法中我们用空间换时间,需要借助一个额外的数组;
• 第二种解法我们把矩阵分成了一层一层,然后逐层旋转;
• 第三种解法我们先沿着对角线翻转,再沿着中轴线翻转。
其实就在于找到元素下标反转前后的关系。
假设矩阵的大小为 n × n,元素位于原矩阵中的位置为 (i, j),那么在顺时针旋转 90 度后,
元素的新位置应该为 (j, n - 1 - i)。
力扣链接:https://leetcode.cn/problems/rotate-image/