题目
给定一个 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]]
题解
辅助数组
核心思想
图像顺时针旋转90度,其实有点像矩阵的转置,不过是把第一行放到最后一列,第二行放到倒数第二列,以此类推。
第一行:
1∘∘2∘∘3∘∘旋转后∘∘∘∘∘∘123
第二行:
∘4∘∘5∘∘6∘旋转后∘∘∘456∘∘∘
第三行:
∘∘7∘∘8∘∘9旋转后789∘∘∘∘∘∘
总结推导:
a11a21⋮an1a12a22⋮an2⋯⋯⋱⋯a1na2n⋮ann旋转后an1an2⋮ann⋯⋯⋱⋯a21a22⋮a2na11a12⋮a1n
翻译成代码:matrix[row][col] 的位置会变成matrixnew[col][n−row−1]
代码
class Solution {
public:
void rotate(vector<vector<int>>& matrix) {
int n = matrix.size();
vector<vector<int>> matrixNew(n, vector<int> (n));
for(int i = 0; i < n; i++) {
for(int j = 0; j < n; j++) {
matrixNew[j][n - i - 1] = matrix[i][j];
}
}
matrix = matrixNew;
}
};
时间复杂度:O(n2),其中 n 是矩阵的边长。
空间复杂度:O(n2),需要一个和 matrix 一样大的矩阵。
原地旋转
核心思想
1. 普通坐标系(数学 xy 坐标系)
数学坐标系:原点左下角,y↑向上增大
矩阵数组坐标系:原点左上角,i↓向下增大
同样一个屏幕上的点:
- 数学坐标:y越大,点越靠上
- 矩阵下标:i越大,点越靠下
{矩阵坐标系:i 向下增大数学笛卡尔坐标系:y 向上增大⇒竖直镜像翻转
矩阵左上角是原点(0,0);数学坐标系左上角是(0,n−1)。
y=(n−1)−i 的来源
举 n=3,n−1=2:
| 矩阵行号i | 在矩阵中的位置 | 想要得到的数学y |
|---|
| i=0 | 最上面一行 | y=2 |
| i=1 | 中间行 | y=1 |
| i=2 | 最下面一行 | y=0 |
观察表格:
i=0→y=2
i=1→y=1
i=2→y=0
可以看到:i+y=n−1 移项直接得到:
y=(n−1)−i
几何理解:上下镜像反射
一条线段,下标 0∼L,点 x 关于中线的镜像点:
x′=L−x
这里 L=n−1,就是最大下标。
矩阵的行 i,以整个矩阵的上下边界做镜像,得到数学坐标的 y。
2. 坐标转换三步法(标准推导)
设矩阵阶数n,下标0∼n−1。 原矩阵位置:矩阵坐标 Pm=(i,j)
1️⃣ 矩阵坐标 → 数学笛卡尔坐标(y 向上) 把矩阵向下的i翻转成向上的 y:
x=j,y=n−1−i
矩阵(i,j) 对应数学点(x,y)=(j, n−1−i)
2️⃣ 数学坐标系点顺时针旋转 90° 旋转公式 (x,y)顺90°(y,−x) 代入得到旋转后的数学坐标(x1,y1):
x1=y=n−1−i,y1=−x=−j
3️⃣ 把旋转后的数学坐标,转换回矩阵坐标(y 轴向下) 数学坐标(x1,y1)变回矩阵(i′,j′) 转换关系:
j′=x1,i′=n−1−y1
代入 x1=n−1−i, y1=−j:
j′i′=n−1−i=n−1−(−j)=j
得到旋转后的矩阵坐标:
(i′,j′)=(j, n−1−i)
✅这就是核心映射:矩阵中(i,j)顺时针转 90 度后,落到位置 (j, n−1−i)。
⎩⎨⎧矩阵坐标(i,j)转数学坐标{x=jy=n−1−i(x,y)顺时针90°旋转(x1,y1)=(y,−x)(x1,y1)转回矩阵坐标{j′=x1i′=n−1−y1⇒(i′,j′)=(j, n−1−i)
3. 得到完整 4 点闭环
已知变换函数 f(i,j)=(j, n−1−i)
- P0=(i,j)
- P1=f(P0)=(j, n−1−i)
- P2=f(P1)=f(j,n−1−i)=(n−1−i, n−1−j)
- P3=f(P2)=f(n−1−i,n−1−j)=(n−1−j, i)
- P4=f(P3)=(i,j)=P0,回到起点,闭环。
(i,j)→(j,n−1−i)→(n−1−i,n−1−j)→(n−1−j,i)→(i,j)
实例验证 n=3
n=3,n−1=2,取P0=(0,0)
- P0=(0,0)
- P1=f(0,0)=(0,2)
- P2=f(0,2)=(2,2)
- P3=f(2,2)=(2,0)
- P4=f(2,0)=(0,0)
对应矩阵四个角:(0,0)→(0,2)→(2,2)→(2,0)→(0,0),完全符合。
147258369
4. 枚举哪些点
不能遍历全部矩阵,否则同一组 4 个点会被重复处理,覆盖已经交换好的值。
只枚举左上角子区域:
⎩⎨⎧0≤i<⌊2n⌋0≤j<⌈2n⌉
⌈2n⌉=2n+1(整数除法)
- n偶数:左上角 2n×2n 的区域。
- n奇数:包含中间列,矩阵中心点不会被枚举,无需处理。
图示:黄色为枚举起点
n=3:
🟨 🟨 ⬜
⬜ ⬜ ⬜
⬜ ⬜ ⬜
n=4:
🟨 🟨 ⬜ ⬜
🟨 🟨 ⬜ ⬜
⬜ ⬜ ⬜ ⬜
⬜ ⬜ ⬜ ⬜
代码
class Solution {
public:
void rotate(vector<vector<int>>& matrix) {
int n = matrix.size();
for(int i = 0; i < n / 2; i++) {
for(int j = 0; j < (n + 1) / 2; j++) {
int temp = matrix[i][j];
matrix[i][j] = matrix[n - j - 1][i];
matrix[n - j - 1][i] = matrix[n - i - 1][n - j - 1];
matrix[n - i - 1][n - j - 1] = matrix[j][n - i - 1];
matrix[j][n - i - 1] = temp;
}
}
}
};
时间复杂度:O(n2),其中 n 是矩阵的边长。
空间复杂度:O(1),原地旋转。
转置 + 每行反转
核心思想
矩阵顺时针旋转 90° = 主对角线转置,再每一行左右翻转。
本质:把旋转拆解成两个简单矩阵变换,避开四点轮换复杂的坐标闭环与循环边界。
- 主对角线转置
主对角线:左上到右下,i=j。 转置操作:交换 matrix[i][j]↔matrix[j][i]。
⚠️注意:只处理上三角区域 j>i。 如果全部 i,j 循环,每一对元素会交换两次,变回原始矩阵。
示例 n=3: 原矩阵
147258369主对角线转置123456789
- 每行左右原地反转
把转置完成后的矩阵,每一行做镜像左右翻转。
123456789每行反转789456123
得到顺时针旋转 90° 的结果。
为什么这两步组合等价顺时针 90°(简单推导)
设原矩阵元素 M[i][j]。
- 主对角线转置之后:M[i][j] 跑到 M[j][i]。
- 行反转:同一行中,列 j 映射到 n−1−j。
合并两步:
- 第一步转置:(i,j)⇒(j,i)
- 第二步行翻转:列下标 i⇒n−1−i
最终位置:
(i,j)⇒(j, n−1−i)
(i,j)原转置(j,i)行反转(j, n−1−i)
✅和四点轮换的核心坐标映射公式完全一致。
也就是说:转置 + 行反转,本质就是间接实现了 (i,j)→(j,n−1−i) 的坐标变换。
代码
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());
}
}
};
时间复杂度:O(n2),每个元素访问常数次。
空间复杂度:O(1),原地操作。
三种算法对比
| 算法 | 是否原地 | 时间复杂度 | 额外空间 | 优点 | 缺点 | 适合场景 |
|---|
| 辅助矩阵拷贝 | ❌ | O(n2) | O(n2) | 逻辑最简单,零边界错误 | 不满足原地要求 | 理解坐标映射,教学演示 |
| 四点轮换交换 | ✅ | O(n2) | O(1) | 体现完整坐标变换原理 | 循环边界易错,代码繁琐 | 面试手撕、原理考察 |
| 转置 + 每行反转 | ✅ | O(n2) | O(1) | 代码简短,边界简单 | 看不到旋转闭环,依赖 reverse | 笔试做题、快速 AC |
hot100_旋转图像
旋转图像题解
分类:算法
标签:hot100 算法