1. 题目解析与核心思路
LeetCode 48题"旋转图像"是一个经典的二维数组操作问题。题目要求将一个n×n的矩阵顺时针旋转90度,并且必须在原地完成操作,即不使用额外的二维数组空间。这道题看似简单,但考察了程序员对矩阵操作和空间复杂度的理解能力。
1.1 题目要求详解
给定一个n×n的二维矩阵matrix,我们需要将其顺时针旋转90度。旋转后的矩阵应该满足:
- 原矩阵的第i行第j列元素 → 旋转后成为新矩阵的第j行第(n-i-1)列元素
示例输入:
code复制[
[1,2,3],
[4,5,6],
[7,8,9]
]
示例输出:
code复制[
[7,4,1],
[8,5,2],
[9,6,3]
]
1.2 关键限制条件
题目有两个重要限制:
- 必须原地旋转,不能使用额外的二维数组空间(只能使用O(1)的额外空间)
- 矩阵的边长n范围是1到20
2. 解法一:使用辅助矩阵(理解旋转规律)
2.1 基本思路
最直观的解法是创建一个新的n×n矩阵res,然后按照旋转规律将原矩阵的元素复制到新矩阵中。具体来说:
code复制res[j][n - i - 1] = matrix[i][j]
即原矩阵的(i,j)元素会移动到新矩阵的(j, n-i-1)位置。
2.2 Java实现代码
java复制class Solution {
public void rotate(int[][] matrix) {
int n = matrix.length;
int[][] res = new int[n][n];
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
res[j][n - i - 1] = matrix[i][j];
}
}
// 把结果复制回原矩阵
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
matrix[i][j] = res[i][j];
}
}
}
}
2.3 复杂度分析
| 项目 | 复杂度 | 说明 |
|---|---|---|
| 时间复杂度 | O(n²) | 需要遍历矩阵的每个元素 |
| 空间复杂度 | O(n²) | 需要创建一个新的n×n矩阵 |
2.4 优缺点分析
优点:
- 思路直观,容易理解和实现
- 代码逻辑简单,不易出错
缺点:
- 不满足题目要求的原地旋转条件
- 空间复杂度较高,当n较大时会消耗较多内存
3. 解法二:原地旋转(推荐解法)
3.1 核心思路
原地旋转的关键在于找到一种不需要额外空间的旋转方法。我们可以通过以下两步实现:
- 矩阵转置:将矩阵沿主对角线翻转,即matrix[i][j] ↔ matrix[j][i]
- 每行反转:将矩阵的每一行进行反转
这两步操作完成后,矩阵就相当于顺时针旋转了90度。
3.2 操作过程演示
原始矩阵:
code复制1 2 3
4 5 6
7 8 9
第一步:转置矩阵
code复制1 4 7
2 5 8
3 6 9
第二步:每行反转
code复制7 4 1
8 5 2
9 6 3
3.3 Java实现代码
java复制class Solution {
public void rotate(int[][] matrix) {
int n = matrix.length;
// Step 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;
}
}
// Step 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 - j - 1];
matrix[i][n - j - 1] = temp;
}
}
}
}
3.4 复杂度分析
| 项目 | 复杂度 | 说明 |
|---|---|---|
| 时间复杂度 | O(n²) | 每个元素最多被处理两次 |
| 空间复杂度 | O(1) | 只使用了常数级别的额外空间 |
3.5 实现细节与注意事项
- 转置操作的范围:转置时只需要处理矩阵的上三角区域(j从i开始),否则会交换两次导致矩阵恢复原状
- 行反转的范围:每行只需要反转前一半元素,否则会反转两次导致行恢复原状
- 边界条件处理:当n为奇数时,中间元素不需要反转
4. 方法对比与选择
4.1 两种方法对比
| 方法 | 额外空间 | 优点 | 缺点 |
|---|---|---|---|
| 辅助矩阵法 | O(n²) | 思路直观,简单易懂 | 不满足"原地"要求 |
| 原地转置+反转 | O(1) | 符合题意,最高效 | 需小心索引处理 |
4.2 选择建议
- 在面试或考试中,推荐使用原地旋转方法,因为它满足题目要求且效率更高
- 在实际开发中,如果空间不是问题,辅助矩阵法可能更易于维护和理解
- 对于特别大的矩阵(虽然本题n≤20),原地方法的内存优势会更明显
5. 常见问题与调试技巧
5.1 常见错误
- 双重交换问题:在转置时如果遍历整个矩阵而不是上三角区域,会导致元素被交换两次,最终矩阵不变
- 索引越界:在反转行时,忘记n/2会导致数组越界
- 边界条件处理不当:对于n=1的矩阵,需要特殊处理吗?(实际上不需要)
5.2 调试技巧
- 打印中间结果:在转置和反转后打印矩阵,验证每一步是否正确
- 小矩阵测试:先用3×3或4×4的矩阵手动计算预期结果,再与程序输出对比
- 边界测试:测试n=1和n=20的情况,确保程序在各种情况下都能正常工作
5.3 性能优化
虽然题目给定的n范围较小,但我们可以考虑以下优化:
- 循环展开:对于小矩阵,可以手动展开循环减少循环开销
- 并行处理:对于大矩阵,转置和反转操作可以并行处理不同行
- SIMD指令:利用现代CPU的SIMD指令加速矩阵操作
6. 扩展与应用
6.1 其他旋转角度
掌握了顺时针90度旋转后,我们可以扩展到其他角度:
- 逆时针90度:先转置,再每列反转
- 180度:可以连续旋转两次90度,或者直接中心对称交换
6.2 实际应用场景
矩阵旋转在以下场景中有实际应用:
- 图像处理:旋转图片时需要对像素矩阵进行操作
- 计算机图形学:3D物体的旋转可以分解为矩阵操作
- 数值计算:某些矩阵运算需要先进行旋转或转置
6.3 相关题目推荐
- LeetCode 54. 螺旋矩阵
- LeetCode 59. 螺旋矩阵 II
- LeetCode 73. 矩阵置零
- LeetCode 74. 搜索二维矩阵
7. 个人经验分享
在实际编程中,处理矩阵旋转问题时我有以下几点经验:
- 画图辅助理解:在纸上画出小矩阵的旋转过程,能帮助理解元素位置的变化规律
- 分步验证:先实现辅助矩阵法验证思路正确性,再优化为原地算法
- 注意索引处理:矩阵操作中最容易出错的就是索引计算,要特别注意边界条件
- 测试用例设计:除了常规测试用例,还要考虑n=1、n=2等边界情况
对于这道题,原地旋转的"转置+反转"方法是一个经典技巧,建议牢记并理解其原理。这种分步转换的思想在其他问题中也很常见,比如某些字符串处理问题可以分解为多个简单操作。