旋转图像 图解题解
这道题到底在问什么
- 输入
- [[1,2,3,4],[5,6,7,8],[9,10,11,12],[13,14,15,16]]
- 输出
- [[13,9,5,1],[14,10,6,2],[15,11,7,3],[16,12,8,4]]
最优解:为什么这么做
一句话答案:LeetCode 48 旋转图像的原地解法是「转置 + 每行反转」两步:先沿主对角线转置,交换 matrix[i][j] 和 matrix[j][i](j 只从 i+1 扫上三角),再把每一行左右反转,两步叠加恰好等于顺时针旋转 90°。全程只用一个临时变量做交换,时间 O(n²)、空间 O(1)。
旋转图像难在哪:原地这个限制
把 n×n 矩阵顺时针转 90°,如果允许开一个新矩阵,一行公式就完事:新矩阵第 i 行第 j 列放原矩阵第 n-1-j 行第 i 列的值。这道题的全部难度在于「原地」——不许借助第二个二维数组,所有值必须在原矩阵内部腾挪。直接照公式搬会边搬边覆盖还没读的值,所以需要换一种分解方式。
为什么想到转置加反转,而不是硬搬公式
原地做旋转的思路有两条。一条是「四点循环交换」:每次同时转动关于中心对称的四个格子,用一个临时变量倒手,能做但下标推导繁琐、极易写错。另一条更漂亮:把旋转拆成两个各自天然原地的对称操作——转置和行反转。
转置就是沿主对角线(左上到右下)翻折,只需成对交换 matrix[i][j] 和 matrix[j][i];行反转就是一行内首尾向中间逐对交换。两个操作都只涉及「一对格子互换」,一个临时变量就够,没有任何覆盖风险。难写的旋转被换成了两个不会错的翻转,这就是这个解法的价值。
为什么转置加每行反转恰好等于顺时针旋转 90 度
盯住一行看:顺时针转 90° 后,新矩阵的第 i 行等于原矩阵的第 i 列「从下往上」读。转置正好把原来的第 i 列搬成第 i 行,但顺序是「从上往下」的;再把这一行左右反转,顺序就倒成「从下往上」——与旋转的结果逐格吻合。两步各管一半:转置负责行列互换,反转负责调正方向,缺一不可。只做转置得到的是沿对角线的镜像翻折,不是旋转。
转置的循环为什么 j 要从 i+1 开始
转置是「成对」交换,每一对只能换一次。如果 j 从 0 扫到 n-1,格子 (i,j) 和 (j,i) 会先后各触发一次交换,换了又换回去,矩阵原封不动——这是本题最经典的隐形 bug,程序不报错、结果全错。让 j 从 i+1 开始,只扫主对角线上方的上三角,每对恰好处理一次;对角线上的格子转置后位置不变,天然跳过。
复杂度多少,逆时针又该怎么转
时间 O(n²):转置扫上三角约 n²/2 对,行反转再扫约 n²/2 对,每个格子只被碰常数次。空间 O(1):除了交换用的临时变量,不占任何额外存储,这正是题目要的原地修改。
方向问题值得单独记一下:顺时针 90° = 转置后反转每一行;逆时针 90° = 先反转每一行再转置(或转置后反转每一列)。两步的顺序或反转对象一换,旋转方向就反了——面试写完可以拿左上角那个元素心算一步验方向,防止顺逆记反。
▶ 动画逐步走查(共 22 步)——想跟着动画一帧帧对照就展开
- 3记住这条恒等式:转置 + 每行反转 = 顺时针 90°。下面一对一对地交换给你看。
- 4第一步「转置」:沿主对角线(左上→右下,绿色那条)把矩阵翻折,对角线上的格子原地不动,只交换它两侧对称的格子 matrix[i][j] ↔ matrix[j][i]。
- 5交换第 1 对:matrix[0][1]=5 和 matrix[1][0]=2 互换(它们关于主对角线对称)。换完这两格的值就对调了。
- 6交换第 2 对:matrix[0][2]=9 和 matrix[2][0]=3 互换(它们关于主对角线对称)。换完这两格的值就对调了。
- 7交换第 3 对:matrix[0][3]=13 和 matrix[3][0]=4 互换(它们关于主对角线对称)。换完这两格的值就对调了。
- 8交换第 4 对:matrix[1][2]=10 和 matrix[2][1]=7 互换(它们关于主对角线对称)。换完这两格的值就对调了。
- 9交换第 5 对:matrix[1][3]=14 和 matrix[3][1]=8 互换(它们关于主对角线对称)。换完这两格的值就对调了。
- 10交换第 6 对:matrix[2][3]=15 和 matrix[3][2]=12 互换(它们关于主对角线对称)。换完这两格的值就对调了。
- 11转置完成:现在每个 matrix[i][j] 都和原来的 matrix[j][i] 换好了位置。但这还不是最终答案——观察会发现每一行的顺序是反的,需要第二步把每行左右翻过来。
- 12开始反转第 0 行:把这一行首尾向中间逐对交换(matrix[0][左] ↔ matrix[0][右])。反转后这一行就落到最终旋转位置了。
- 13第 0 行:列 0 的 1 和 列 3 的 13 互换。每交换一对,这一行就向「左右翻转」前进一步。
- 14第 0 行:列 1 的 5 和 列 2 的 9 互换。每交换一对,这一行就向「左右翻转」前进一步。
- 15开始反转第 1 行:把这一行首尾向中间逐对交换(matrix[1][左] ↔ matrix[1][右])。反转后这一行就落到最终旋转位置了。
- 16第 1 行:列 0 的 2 和 列 3 的 14 互换。每交换一对,这一行就向「左右翻转」前进一步。
- 17第 1 行:列 1 的 6 和 列 2 的 10 互换。每交换一对,这一行就向「左右翻转」前进一步。
- 18开始反转第 2 行:把这一行首尾向中间逐对交换(matrix[2][左] ↔ matrix[2][右])。反转后这一行就落到最终旋转位置了。
- 19第 2 行:列 0 的 3 和 列 3 的 15 互换。每交换一对,这一行就向「左右翻转」前进一步。
- 20第 2 行:列 1 的 7 和 列 2 的 11 互换。每交换一对,这一行就向「左右翻转」前进一步。
- 21开始反转第 3 行:把这一行首尾向中间逐对交换(matrix[3][左] ↔ matrix[3][右])。反转后这一行就落到最终旋转位置了。
- 22第 3 行:列 0 的 4 和 列 3 的 16 互换。每交换一对,这一行就向「左右翻转」前进一步。
- 23第 3 行:列 1 的 8 和 列 2 的 12 互换。每交换一对,这一行就向「左右翻转」前进一步。
- 24全部完成!转置(6 对)+ 每行反转(8 对)共 14 次原地交换后,矩阵就顺时针旋转了 90°,且没有用到任何额外矩阵——这就是原地旋转。
⚠️ 容易写错的地方
✗ 错:转置时 j 从 0 开始遍历
✓ 对:j 从 i+1 开始(只扫上三角)
j 从 0 会把每对换两次 = 等于没换,矩阵原封不动
✗ 错:转置后忘了反转每一行
✓ 对:转置 + 每行反转两步缺一不可
只转置得到的是「沿对角线翻折」,不是旋转
✗ 错:把「逆时针」和「顺时针」记反
✓ 对:顺时针=转置后反转每行;逆时针=反转每行后转置(或转置后反转每列)
步骤顺序/反转对象不同,结果就转反方向了
完整代码(Python / C++ / Java)
Python
def rotate(matrix):
n = len(matrix)
# ① 沿主对角线转置:matrix[i][j] <-> matrix[j][i]
for i in range(n):
for j in range(i + 1, n):
matrix[i][j], matrix[j][i] = matrix[j][i], matrix[i][j]
# ② 每一行左右反转
for row in matrix:
row.reverse()C++
class Solution {
public:
void rotate(vector<vector<int>>& matrix) {
int n = matrix.size();
// ① 转置
for (int i = 0; i < n; i++)
for (int j = i + 1; j < n; j++)
swap(matrix[i][j], matrix[j][i]);
// ② 每行反转
for (int i = 0; i < n; i++)
reverse(matrix[i].begin(), matrix[i].end());
}
};Java
class Solution {
public void rotate(int[][] matrix) {
int n = matrix.length;
// ① 沿主对角线转置
for (int i = 0; i < n; i++) {
for (int j = i + 1; j < n; j++) {
int t = matrix[i][j];
matrix[i][j] = matrix[j][i];
matrix[j][i] = t;
}
}
// ② 每一行左右反转
for (int i = 0; i < n; i++) {
int lo = 0, hi = n - 1;
while (lo < hi) {
int t = matrix[i][lo];
matrix[i][lo] = matrix[i][hi];
matrix[i][hi] = t;
lo++; hi--;
}
}
}复杂度
时间
O(n²)
转置和反转各扫一遍矩阵,共约 n² 次操作
空间
O(1)
只用一个临时变量做交换,不开新矩阵
看不够?换成动画再走一遍
上面的推演每一步都对应一帧动画。点开交互动画版,能一步步看着 旋转图像 的数据怎么变、指针怎么走,还能切 Python / Java / C++ 跟着练。
面试官可能追问
为什么转置 + 每行反转就等于顺时针旋转 90°?+
旋转后新矩阵的第 i 行 = 原矩阵第 i 列从下往上读。转置先把「第 i 列」变成「第 i 行」(但顺序是从上往下),再把这一行左右反转,就变成「从下往上」的顺序,正好等于旋转结果。
如果要逆时针旋转 90° 怎么改?+
两种等价改法:① 先转置再反转每一「列」;② 或先反转每一行再转置。本质是把第二步的反转对象/顺序调一下,方向就反过来。
想听吴师兄把这道题讲给你听?
文字版和动画都随便看。开通图解算法年卡,可以听吴师兄把 旋转图像 一步步讲透(全站已上线 905 份讲透,持续扩充), 卡住的地方还有 AI 私教小欧就着动画帮你拆到懂。
本平台为独立第三方培训机构,与华为技术有限公司无任何关联;课程的服务内容与权益以购买协议为准,学习效果因个人情况而异。「华为 OD」「华为可信」等仅为对岗位与考试方向的客观描述,相关商标归各自权利人所有。