题目描述
思路解析
一句话答案: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° = 先反转每一行再转置(或转置后反转每一列)。两步的顺序或反转对象一换,旋转方向就反了——面试写完可以拿左上角那个元素心算一步验方向,防止顺逆记反。
▶ 动画逐步走查(文字版)——想跟着上方动画一帧帧对照就展开
记住这条恒等式:转置 + 每行反转 = 顺时针 90°。下面一对一对地交换给你看。
第一步「转置」:沿主对角线(左上→右下,绿色那条)把矩阵翻折,对角线上的格子原地不动,只交换它两侧对称的格子 matrix[i][j] ↔ matrix[j][i]。
交换第 1 对:matrix[0][1]=5 和 matrix[1][0]=2 互换(它们关于主对角线对称)。换完这两格的值就对调了。
交换第 2 对:matrix[0][2]=9 和 matrix[2][0]=3 互换(它们关于主对角线对称)。换完这两格的值就对调了。
交换第 3 对:matrix[0][3]=13 和 matrix[3][0]=4 互换(它们关于主对角线对称)。换完这两格的值就对调了。
交换第 4 对:matrix[1][2]=10 和 matrix[2][1]=7 互换(它们关于主对角线对称)。换完这两格的值就对调了。
交换第 5 对:matrix[1][3]=14 和 matrix[3][1]=8 互换(它们关于主对角线对称)。换完这两格的值就对调了。
交换第 6 对:matrix[2][3]=15 和 matrix[3][2]=12 互换(它们关于主对角线对称)。换完这两格的值就对调了。
转置完成:现在每个 matrix[i][j] 都和原来的 matrix[j][i] 换好了位置。但这还不是最终答案——观察会发现每一行的顺序是反的,需要第二步把每行左右翻过来。
开始反转第 0 行:把这一行首尾向中间逐对交换(matrix[0][左] ↔ matrix[0][右])。反转后这一行就落到最终旋转位置了。
第 0 行:列 0 的 1 和 列 3 的 13 互换。每交换一对,这一行就向「左右翻转」前进一步。
第 0 行:列 1 的 5 和 列 2 的 9 互换。每交换一对,这一行就向「左右翻转」前进一步。
开始反转第 1 行:把这一行首尾向中间逐对交换(matrix[1][左] ↔ matrix[1][右])。反转后这一行就落到最终旋转位置了。
第 1 行:列 0 的 2 和 列 3 的 14 互换。每交换一对,这一行就向「左右翻转」前进一步。
第 1 行:列 1 的 6 和 列 2 的 10 互换。每交换一对,这一行就向「左右翻转」前进一步。
开始反转第 2 行:把这一行首尾向中间逐对交换(matrix[2][左] ↔ matrix[2][右])。反转后这一行就落到最终旋转位置了。
第 2 行:列 0 的 3 和 列 3 的 15 互换。每交换一对,这一行就向「左右翻转」前进一步。
第 2 行:列 1 的 7 和 列 2 的 11 互换。每交换一对,这一行就向「左右翻转」前进一步。
开始反转第 3 行:把这一行首尾向中间逐对交换(matrix[3][左] ↔ matrix[3][右])。反转后这一行就落到最终旋转位置了。
第 3 行:列 0 的 4 和 列 3 的 16 互换。每交换一对,这一行就向「左右翻转」前进一步。
第 3 行:列 1 的 8 和 列 2 的 12 互换。每交换一对,这一行就向「左右翻转」前进一步。
全部完成!转置(6 对)+ 每行反转(8 对)共 14 次原地交换后,矩阵就顺时针旋转了 90°,且没有用到任何额外矩阵——这就是原地旋转。
n=1 是天然的恒等情形,不需要特判。
理解了「转置=行列对调、反转=补上方向」,正反两个方向都能现推。
参考代码
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()复杂度
- 时间:O(n²),转置和反转各扫一遍矩阵,共约 n² 次操作
- 空间:O(1),只用一个临时变量做交换,不开新矩阵
易错点
面试追问把动画讲成自己的话
追问为什么转置 + 每行反转就等于顺时针旋转 90°?
追问如果要逆时针旋转 90° 怎么改?
这道题到这就讲完了。动画和文字是同一套思路——别光看,关掉页面自己默写一遍。然后顺着主线继续:
螺旋矩阵
LeetCode 54 · 中等 · 沿着 数学 & 几何 继续往下推进
把这道题真正学会,再走
图解算法年卡 ¥99 /年
- ✓本题每步动画的吴师兄语音讲解(全站陆续覆盖)
- ✓小欧带 8 步通关训练——追问到不看答案也能写对、能 30 秒讲给面试官听
- ✓学习报告,记录每道题的掌握程度
76k+ GitHub Star · 吴师兄开源图解算法,几十万开发者在看的算法讲解
想成体系刷透这类套路?去图解算法专题