48. 旋转图像#
生活中的旋转#
在这个自拍时代,我们经常需要调整照片的方向。有时拍出来的照片歪了,需要旋转90度;有时想要换个角度看看效果,来回旋转照片。这种旋转操作不仅存在于我们的日常生活中,在计算机图形学、图像处理等领域也是一个基础且重要的操作。
问题描述#
题目目标#
LeetCode第48题”旋转图像”要求我们:给定一个 n × n 的二维矩阵 matrix 表示一个图像,将图像顺时针旋转 90 度。要求必须在原地旋转图像,也就是说,你需要直接修改输入的二维矩阵。
示例 1#
输入:
matrix = [[1,2,3],
[4,5,6],
[7,8,9]]text输出:
[[7,4,1],
[8,5,2],
[9,6,3]]
就像我们在手机相册里旋转照片一样,每个像素点都要移动到新的位置,但我们需要保证不使用额外的存储空间!text矩阵示意:
1 2 3
4 5 6
7 8 9text说明: 图示把输入结构和输出结果并排展开后,会更容易直接看清题目的变化过程。
补充说明#
- 矩阵按行给出,外层数组表示所有行,内层数组表示每一行的元素。
最直观的解法:辅助数组#
最简单的想法就像我们复印一张照片,在新的纸上重新排列像素。虽然这种方法使用了额外空间,不符合题目要求,但它帮助我们理解旋转的本质。
辅助数组的实现#
public void rotate(int[][] matrix) {
// n 表示矩阵的行数/列数(题目保证是 n x n 的方阵)
int n = matrix.length;
// temp 是辅助数组,用于存放旋转后的结果(该解法空间复杂度为 O(n^2))
int[][] temp = new int[n][n];
// 遍历原矩阵中的每个位置 (i, j)
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
// 关键映射关系:
// 原位置 (i, j) 在顺时针旋转 90° 后,移动到 (j, n - 1 - i)
temp[j][n-1-i] = matrix[i][j];
}
}
// 再次遍历,把辅助数组中的结果拷贝回原矩阵(满足函数“就地修改入参”的接口要求)
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
// 用旋转后的值覆盖原位置
matrix[i][j] = temp[i][j];
}
}
}java优化解法:原地旋转#
仔细观察,我们发现90度旋转可以通过两步简单的操作完成:先沿对角线翻转,再沿竖直中线翻转。就像折纸一样,通过两次折叠就能达到旋转的效果!
原地旋转的原理#
想象你在玩魔方:
- 第一步:沿主对角线翻转(左上到右下的对角线)
- [i,j] 变成 [j,i]
- 第二步:沿竖直中线翻转
- [i,j] 变成 [i,n-1-j]
示例运行#
用3×3矩阵来说明:
原始矩阵: 对角线翻转: 竖直中线翻转:
1 2 3 1 4 7 7 4 1
4 5 6 → 2 5 8 → 8 5 2
7 8 9 3 6 9 9 6 3
第一步:对角线翻转
- (1,2)和(2,1)交换
- (1,3)和(3,1)交换
- (2,3)和(3,2)交换
第二步:竖直中线翻转
- 第1列和第3列交换
- 第2列保持不变plaintextJava代码实现#
public void rotate(int[][] matrix) {
// n 表示方阵边长
int n = matrix.length;
// 步骤1:先做“转置”(沿主对角线翻转)
// 把 matrix[i][j] 与 matrix[j][i] 交换
for (int i = 0; i < n; i++) {
// j 从 i 开始,避免重复交换(例如 (1,2) 与 (2,1) 只交换一次)
for (int j = i; j < n; j++) {
// temp 临时保存一个值,防止交换时数据丢失
int temp = matrix[i][j];
// 把对称位置的值放到当前位置
matrix[i][j] = matrix[j][i];
// 把暂存值放回对称位置,完成一次交换
matrix[j][i] = temp;
}
}
// 步骤2:再做“左右翻转”(沿竖直中线翻转)
// 每一行中,第 j 列与第 (n - 1 - j) 列交换
for (int i = 0; i < n; i++) {
// 只需要遍历一半列;另一半会在交换中同步完成
for (int j = 0; j < n/2; j++) {
// temp 临时保存左侧值
int temp = matrix[i][j];
// 右侧值移到左侧
matrix[i][j] = matrix[i][n-1-j];
// 左侧原值移到右侧,完成对称交换
matrix[i][n-1-j] = temp;
}
}
}java解法比较#
让我们比较这两种方法:
辅助数组法:
- 时间复杂度:O(n²)
- 空间复杂度:O(n²)
- 优点:直观易懂,容易实现
- 缺点:需要额外空间,不满足原地旋转的要求
原地旋转法:
- 时间复杂度:O(n²)
- 空间复杂度:O(1)
- 优点:不需要额外空间,完美满足题目要求
- 缺点:需要理解矩阵变换的数学原理
实用技巧总结#
解决矩阵旋转问题的关键点:
- 观察旋转前后元素位置的对应关系
- 寻找可以分解的子操作(如翻转)
- 正确处理边界情况
- 小心不要重复交换元素
相关的矩阵变换问题:
- 矩阵转置
- 矩阵对称变换
- 顺时针/逆时针旋转任意角度
小结#
通过旋转图像这道题,我们学会了如何通过巧妙的数学变换来完成矩阵旋转。这种思维方式不仅能解决算法题,在图像处理、计算机图形学等领域都有广泛应用。记住,当遇到需要变换矩阵的问题时,可以考虑将复杂的变换分解为简单的操作组合,这样往往能得到更优雅的解决方案!