---
description: "顺时针转 90° = 沿主对角线转置 + 每行左右翻转；把旋转拆成两个自逆的对称操作，原地就好写"
---

# 48. 旋转图像

LeetCode 48[^leetcode] · 中等 · 矩阵

## 题

把一个 `n × n` 矩阵顺时针旋转 90 度，必须原地修改，不能另开一个矩阵。

例：`[[1, 2, 3], [4, 5, 6], [7, 8, 9]]` → `[[7, 4, 1], [8, 5, 2], [9, 6, 3]]`。

## 一句话

先转置（`M[i][j] ↔ M[j][i]`），再把每一行反转。

## 关键技巧

**看坐标怎么变。** 顺时针 90° 把 $(i, j)$ 送到 $(j,\ n-1-i)$。把这个映射拆成两步：

$$
(i, j) \xrightarrow{\text{转置}} (j, i) \xrightarrow{\text{行内翻转}} (j,\ n-1-i)
$$

**两步都是「对合」（自己的逆），原地交换就行。** 转置是沿对角线的对称，只需交换上三角和下三角（`j > i`）；行翻转是每行左右对称交换。对称操作两两配对互换，不需要临时缓冲，天然 $O(1)$ 空间。直接做旋转则是四个元素一轮的环，要一个临时变量、下标更难写。

**组合表：**

| 目标 | 做法 |
| --- | --- |
| 顺时针 90° | 转置 + 每行翻转 |
| 逆时针 90° | 转置 + 上下翻转（或每行翻转 + 转置） |
| 180° | 上下翻转 + 每行翻转 |

## 解

```python
class Solution:
    def rotate(self, matrix: List[List[int]]) -> None:
        n = len(matrix)
        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^2)$，空间 $O(1)$。

## 延伸

- 四元素轮换法：按层处理，每层对 `(i, j) → (j, n-1-i) → (n-1-i, n-1-j) → (n-1-j, i)` 四个位置转一圈，每个元素只移动一次，但下标很容易写错。
- 按层处理矩阵的同类：[54. 螺旋矩阵](0054-spiral-matrix.md)。
- 坑：转置时内层循环从 `i + 1` 开始，写成 `range(n)` 会把每对交换两次、等于没换。
- Python 里 `matrix[:] = [list(r) for r in zip(*matrix[::-1])]` 一行也对，但开了新空间，不满足原地要求。

??? note "自测"

    ```python
    s = Solution()
    for mat, want in [
        ([[1, 2, 3], [4, 5, 6], [7, 8, 9]], [[7, 4, 1], [8, 5, 2], [9, 6, 3]]),
        (
            [[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]], [[1]]),
        ([[1, 2], [3, 4]], [[3, 1], [4, 2]]),
    ]:
        s.rotate(mat)
        assert mat == want
    ```

[^leetcode]: [LeetCode 48](https://leetcode.cn/problems/rotate-image/)，2026-09-29 读。
