#48 矩阵操作 中等

旋转图像

在 LeetCode 上查看 ↗

🎧 语音讲解

开车或通勤时可听,跟着思路走一遍

速度

📋 题目描述

给定一个 n × n 的二维矩阵 matrix 表示一个图像。请你将图像顺时针旋转 90 度

你必须在原地旋转图像,这意味着你需要直接修改输入的二维矩阵。请不要使用另一个矩阵来旋转图像。

示例 1

输入:matrix = [[1,2,3],[4,5,6],[7,8,9]]
输出:[[7,4,1],[8,5,2],[9,6,3]]

示例 2

输入: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. 最直接:开一个新 n×n 数组,按公式 new[j][n-1-i] = old[i][j] 填值——思路正确,但题目要求原地,额外 O(n²) 空间会被判不符合。

2. 重复在哪里?若逐元素搬到临时变量再写回,本质上仍需要辅助存储;要把「顺时针 90°」拆成可在原数组上完成的原子操作。

3. 关键观察:顺时针 90° = 先转置(沿主对角线交换)再对每一行左右翻转。手画 3×3 例子可验证:转置后 [[1,4,7],[2,5,8],[3,6,9]],逐行翻转即得目标。

4. 坐标规律:原位置 (i,j) 顺时针 90° 后到 (j, n-1-i);转置把 (i,j)→(j,i),行翻转把 (j,i)→(j, n-1-i),两步合成即目标映射。

5. 另一种等价写法是按「同心层」四元组循环交换(每次转 4 个角),但转置+翻转代码更短、不易写错下标;n ≤ 20O(n²) 完全够用。

🧠 变量语义(先读这三句再编码)

变量类型语义(三句法)
matrixlist<list<int>>定义n×n 方阵,既是输入也是最终输出
维护:分两步原地变换——先沿主对角线转置,再对每一行左右翻转
更新:转置交换 matrix[i][j]matrix[j][i];翻转交换行内 matrix[i][left]matrix[i][right]
nint定义:矩阵边长,n = len(matrix)
维护:转置时 i0..n-1ji+1..n-1,避免同一对元素交换两次
更新:全程不变,控制两层循环边界
i, jint定义:转置阶段的双重下标,遍历上三角区域
维护:每对 (i,j) 满足 j > i,交换 matrix[i][j]matrix[j][i]
更新:双重循环递增;转置完成后进入逐行翻转阶段
left, rightint定义:翻转第 i 行时的双指针,分别指向行首与行尾
维护left < right 时交换 matrix[i][left]matrix[i][right],然后 left++right--
更新:相遇时当前行翻转完成,换下一行

⌨️ 落码步骤

1. 取 n = len(matrix)

2. 转置:双重循环 for i in range(n): for j in range(i+1, n): 交换 matrix[i][j]matrix[j][i]

3. 逐行翻转:对每行 i,令 left=0, right=n-1,当 left < right 时交换两端元素并收缩指针

4. 两步完成后 matrix 即为顺时针 90° 结果,无需返回值(原地修改)

💻 代码实现

class Solution:
    def rotate(self, matrix: List[List[int]]) -> None:
        n = len(matrix)
        # 1. 沿主对角线转置
        for i in range(n):
            for j in range(i + 1, n):
                matrix[i][j], matrix[j][i] = matrix[j][i], matrix[i][j]
        # 2. 每一行左右翻转
        for i in range(n):
            left, right = 0, n - 1
            while left < right:
                matrix[i][left], matrix[i][right] = matrix[i][right], matrix[i][left]
                left += 1
                right -= 1
class Solution {
public:
    void rotate(vector<vector<int>>& matrix) {
        int n = matrix.size();
        // 1. 沿主对角线转置
        for (int i = 0; i < n; i++) {
            for (int j = i + 1; j < n; j++) {
                swap(matrix[i][j], matrix[j][i]);
            }
        }
        // 2. 每一行左右翻转
        for (int i = 0; i < n; i++) {
            int left = 0, right = n - 1;
            while (left < right) {
                swap(matrix[i][left], matrix[i][right]);
                left++;
                right--;
            }
        }
    }
};
// 时间 O(n²),空间 O(1)

📈 复杂度分析

时间复杂度 O(n²)
空间复杂度 O(1)(原地修改,不计输入)

⚠️ 常见坑

转置时 j 必须从 i+1 开始,不能从 0 开始——否则同一对元素会被交换两次,等于没转置。

逆时针 90° 是「转置 + 逐列翻转」或「先逐行翻转再转置」,与顺时针步骤不同;混用会得到错误结果。

题目要求原地修改、无返回值;新建矩阵再赋值虽能 AC 部分测试,但不符合题意且浪费空间。

🔍 必测边界 Case

Case 1:1×1 矩阵
matrix = [[1]] → [[1]](转置与翻转均为空操作)
Case 2:2×2 矩阵
matrix = [[1,2],[3,4]] → [[3,1],[4,2]]
Case 3:含负数
matrix = [[-1,2],[-3,4]] → [[-3,-1],[4,2]](符号不影响交换逻辑)
Case 4:奇数边长 3×3
matrix = [[1,2,3],[4,5,6],[7,8,9]] → [[7,4,1],[8,5,2],[9,6,3]](中心元素 5 转置后仍在中心)
Case 5:偶数边长 4×4
见示例 2(无单独中心格,全靠成对交换完成)