NO.48 旋转图像 —— LeetCode 热题 100 面试导向深度解析

题目

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

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

示例 1:

lua 复制代码
输入:matrix = [[1,2,3],[4,5,6],[7,8,9]]
输出:[[7,4,1],[8,5,2],[9,6,3]]

示例 2:

lua 复制代码
输入: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]]

一、读题

一句话复述 :将一个 ( n \times n ) 的方阵原地顺时针旋转 90°,不能借助额外矩阵。

关键约束高亮

  • ( n \times n ) 方阵(行数 = 列数),这是旋转能简化的前提;
  • 原地修改Do not return anything),意味着空间复杂度必须为 ( O(1) )(不计递归栈);
  • 矩阵元素类型为整数,无特殊范围限制。

二、前置知识

序号 知识点 说明
1 矩阵转置 沿主对角线(左上→右下)翻转,即 matrix[i][j]matrix[j][i]
2 水平翻转(行内逆序) 每行左右对称交换,即 matrix[i][j]matrix[i][n-1-j]
3 原地交换技巧 利用 ES6 解构赋值 [a, b] = [b, a] 无需临时变量,简洁且安全

非常见 API 表格

API 作用 参数 返回值 使用场景
Math.floor(n / 2) 向下取整 数值 整数 行内翻转时确定只需遍历前半列
解构赋值 [a, b] = [b, a] 交换两个变量值 左右均为数组/类数组 undefined 原地交换矩阵元素,无需 temp

三、思路推导(核心)

暴力解(不满足题意)

最容易想到的是新建矩阵 :观察可知,旋转后 new[j][n-1-i] = old[i][j],即第 i 行第 j 列的元素会跑到第 j 行倒数第 i 列。

javascript 复制代码
var rotate = function(matrix) {
    const n = matrix.length;
    const newMatrix = Array.from({ length: n }, () => Array(n).fill(0));
    for (let i = 0; i < n; i++) {
        for (let j = 0; j < n; j++) {
            newMatrix[j][n - 1 - i] = matrix[i][j];
        }
    }
    // 拷贝回去(但这不算严格原地,且额外 O(n^2) 空间)
    for (let i = 0; i < n; i++) {
        for (let j = 0; j < n; j++) {
            matrix[i][j] = newMatrix[i][j];
        }
    }
};
  • 时间复杂度:( O(n^2) )(两轮遍历,常数倍忽略);
  • 空间复杂度:( O(n^2) )(额外矩阵)。

代入最大数据量(LeetCode 通常 ( n \le 20 ) 或更大),若 ( n=1000 ),则额外需要 100 万个元素的存储空间,内存可能不足,且违背题意


优化动机 ------ 为什么能想到"转置 + 水平翻转"?

思维演进路径

  1. 观察旋转前后坐标变化(i, j)(j, n-1-i)
  2. 拆解这个变换 :能否分解为两个更简单的操作?
    • 先做转置(i, j)(j, i)
    • 再做水平翻转(j, i)(j, n-1-i)
    • 两步合起来正好是 (i, j)(j, n-1-i)
  3. 为什么这样想 :因为转置和翻转都是原地可完成的操作,且逻辑简单------转置只需遍历对角线一侧,翻转只需每行对折交换。这种"将复杂变换拆成两个已知简单操作"是算法设计的常见套路。

核心几何直觉:顺时针旋转 90° = 沿主对角线翻折 + 左右翻折(或者先上下翻折再转置,两种等价)。


手把手模拟(以 3×3 为例)

矩阵初始:

步骤 操作 矩阵状态
0 原始 \[1,2,3,4,5,6,7,8,9]
1 转置:交换 (0,1)(1,0) \[1,4,3,2,5,6,7,8,9]
2 转置:交换 (0,2)(2,0) \[1,4,7,2,5,6,3,8,9]
3 转置:交换 (1,2)(2,1) \[1,4,7,2,5,8,3,6,9]
4 水平翻转第 0 行:交换 (0,0)(0,2) \[7,4,1,2,5,8,3,6,9]
5 水平翻转第 1 行:交换 (1,0)(1,2) \[7,4,1,8,5,2,3,6,9]
6 水平翻转第 2 行:交换 (2,0)(2,2) \[7,4,1,8,5,2,9,6,3] ✅

四、代码与避坑

完整代码

javascript 复制代码
/**
 * @param {number[][]} matrix
 * @return {void} Do not return anything, modify matrix in-place instead.
 */
var rotate = function(matrix) {
    const n = matrix.length;

    // 1. 转置(沿主对角线翻转)
    // 为什么 j 从 i+1 开始?因为只需遍历上三角,避免重复交换
    for (let i = 0; i < n; i++) {
        for (let j = i + 1; j < n; j++) {
            [matrix[i][j], matrix[j][i]] = [matrix[j][i], matrix[i][j]];
        }
    }

    // 2. 每行左右翻转
    // 为什么 j < Math.floor(n / 2)?因为只需遍历每行前半部分,避免重复交换
    for (let i = 0; i < n; i++) {
        for (let j = 0; j < Math.floor(n / 2); j++) {
            [matrix[i][j], matrix[i][n - 1 - j]] = [matrix[i][n - 1 - j], matrix[i][j]];
        }
    }
};

边界攻防表

边界场景 ❌ 错误写法 ✅ 正确写法 原因
n = 1(单元素矩阵) 内外层循环正常执行,j < n 进入转置,j < Math.floor(n/2) 不执行 转置时 j = i+1 = 1,内层不执行;水平翻转 j < 0 不执行 对角线无元素可交换,行内无需翻转
n = 2(最小非平凡) 转置时 ji+1 开始,正确交换一次;水平翻转 j < 1 交换一次 正确 注意双重循环的边界条件
偶数 n(如 n=4) 水平翻转时 j < n/2j < 2,交换 (0,3) 和 (1,2),正确覆盖全部 正确 偶数行中心是两个元素,需交换 n/2 对

五、举一反三

面试追问

  1. 逆时针旋转 90° 怎么做?

    → 先转置,再上下翻转 (每列上下交换),即 (i,j)(n-1-j, i)

  2. 旋转 180° 怎么做?

    → 先上下翻转,再左右翻转;或者直接 (i,j)(n-1-i, n-1-j) 原地交换,只需遍历四分之一矩阵。

  3. 如果矩阵不是方阵(( m \times n )) ,能否原地旋转?

    → 不能严格原地,因为旋转后维度变为 ( n \times m ),形状改变,必须借助新矩阵。

  4. 如果要求旋转后输出新的矩阵,但原矩阵保留 (非原地)?

    → 直接用暴力解的新建矩阵即可,代码更简单,无需转置翻转。

相似题型

题目 差异点 解法变化
LeetCode 54. 螺旋矩阵 按螺旋顺序遍历输出,不需要修改原矩阵 用四个边界指针收缩,属于模拟题
LeetCode 59. 螺旋矩阵 II 生成一个 ( n \times n ) 的螺旋矩阵 按顺序填充数字,同样用边界指针
剑指 Offer 29. 顺时针打印矩阵 同 54 题,但要求返回一维数组 边界模拟,注意空矩阵处理

核心思路迁移:矩阵变换类问题,优先考虑"分解为转置 + 翻转"的组合,因为这两种操作原地成本低,易于实现。


一句话总结 :顺时针旋转 90° = 转置 + 水平翻转,原地完成,时间复杂度 ( O(n^2) ),空间复杂度 ( O(1) )。遇到类似矩阵变换,先想能否拆解为简单操作的组合。

相关推荐
JieE2121 小时前
LeetCode 138 随机链表的复制|两种解法详解(哈希表 + 原地 O (1) 空间)
javascript·算法·面试
专注于ai算法的踩坑小达人2 小时前
时序预测主流模型综述
算法·lstm
秋天的一阵风2 小时前
🔥 ECMAScript 2026 来了!使用这些新特性,JS 代码直接少一半
前端·javascript·ecmascript 6
前端进阶之旅2 小时前
Next.js 16.3 新特性全解析 AI 驱动开发与 Instant Navigations 实战指南
开发语言·javascript·人工智能
Cobyte2 小时前
编译解析器的基础——状态模式与状态机的原理
前端·javascript·vue.js
HZZD_HZZD2 小时前
碳核查场景下能源计量数据精准采集与碳排放核算算法全链路实战:从智能电表协议适配到排放因子动态计算
算法·能源
饼干哥哥3 小时前
10.5% 深层转化、90%+ 新用户,ChatGPT Ads 值得吗?
算法·正则表达式·编程语言
Irissgwe3 小时前
算法之二分查找
数据结构·c++·算法·二分查找
亿牛云爬虫专家3 小时前
代理IP质量评估:如何建立一套代理IP的多维度评分与淘汰算法?
tcp/ip·算法·爬虫代理·延迟·免费代理ip·连通率·时效