LeetCode 热题 100——矩阵——旋转图像

22. 旋转图像

题目描述

给定一个 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]]

提示:

n == matrix.length == matrix[i].length

1 <= n <= 20

-1000 <= matrix[i][j] <= 1000

求解

(1)使用辅助矩阵

关键思路:matrixnew[col][n−row−1]=matrix[row][col]

js 复制代码
var rotate = function(matrix) {
    let m = matrix.length
    // 用一个辅助矩阵
    let temp = new Array(m).fill(0).map(() => new Array(m).fill(0))
    // 第一行 变为 最后一列; 第二行变为 倒数第二列 。。。
    for (let i = 0; i < m; i++) {
        for (let j = 0; j < m; j++) {
            temp[j][m - i - 1] = matrix[i][j] 
        }
    }
    for (let i = 0; i < m; i++) {
        for (let j = 0; j < m; j++) {
            matrix[i][j] = temp[i][j]
        }
    }
    return matrix
};

(2)原地操作:

核心思路:水平翻转+主对角线翻转

js 复制代码
var rotate = function(matrix) {
    let m = matrix.length
    // 翻转 代替 旋转
    // 先水平翻转 + 再主对角线翻转
    for (let i = 0; i < m / 2; i++) {
        for (let j = 0; j < m; j++) {
            [matrix[i][j], matrix[m - i - 1][j]] = [matrix[m - i - 1][j], matrix[i][j]]
        }
    }
    for (let i = 0; i < m; i++) {
        for (let j = i + 1; j < m; j++) {
            [matrix[i][j], matrix[j][i]] = [matrix[j][i], matrix[i][j]]
        }
    }
    return matrix
};

复习知识点

变量赋值

核心概念:值类型 vs. 引用类型,这是理解 JavaScript 赋值行为的基石。JavaScript 中的数据类型可以分为两大类,它们的赋值规则截然不同。

1. 值类型 (Value Types / Primitive Types)

  • 特点:变量存储的是数据本身。

  • 包括:

    • String (字符串)
    • Number (数字)
    • Boolean (布尔值)
    • null (空值)
    • undefined (未定义)
    • Symbol (ES6 新增)
    • BigInt (ES10 新增)
  • 赋值行为:传值 (Pass by Value)。

    • 当你把一个值类型的变量赋值给另一个变量时,JavaScript 会创建这个值的一个副本,然后将副本赋给新变量 。此后,两个变量是完全独立的,修改其中一个不会影响另一个
    • 示例:
    javascript 复制代码
    let a = 10;
    let b = a; // 将 a 的值 10 复制一份,赋给 b
    
    b = 20; // 修改 b 的值
    
    console.log(a); // 输出: 10 (a 的值不受影响)
    console.log(b); // 输出: 20

2. 引用类型 (Reference Types)

  • 特点:变量存储的是数据在内存中的地址(引用 / 指针),而不是数据本身。

  • 包括:

    • Object (对象)
    • Array (数组)
    • Function (函数)
    • 以及所有通过 new 关键字创建的对象(如 Date, RegExp 等)
  • 赋值行为:传引用 (Pass by Reference)。

    • 当你把一个引用类型的变量赋值给另一个变量时,JavaScript 复制的是这个变量存储的内存地址,然后将这个地址副本赋给新变量。结果是,两个变量指向了内存中的同一个对象。因此,通过任何一个变量修改对象的属性或内容,都会影响到另一个变量
    • 示例:
    javascript 复制代码
    let obj1 = { name: "张三" };
    let obj2 = obj1; // 复制 obj1 存储的内存地址,obj2 也指向同一个对象
    
    obj2.name = "李四"; // 通过 obj2 修改对象的属性
    
    console.log(obj1.name); // 输出: "李四" (obj1 指向的对象也被修改了)
    console.log(obj2.name); // 输出: "李四"
  • 注意:这里的 "修改" 指的是修改对象内部的属性或元素。如果是直接让变量指向一个新的对象,则属于重新赋值,不会影响其他变量。

    javascript 复制代码
    let obj1 = { name: "张三" };
    let obj2 = obj1;
    
    obj2 = { name: "王五" }; // obj2 现在指向一个全新的对象
    
    console.log(obj1.name); // 输出: "张三" (obj1 不受影响)
    console.log(obj2.name); // 输出: "王五"

重要的延伸概念

1. 浅拷贝 (Shallow Copy)

  • 对于引用类型,简单的赋值 (=) 是传引用。如果你想创建一个新的对象,但又想共享原始对象的部分数据,可以使用浅拷贝。

  • 特点:创建一个新的对象,然后将原始对象的属性值(如果是值类型则复制值,如果是引用类型则复制引用)逐一赋给新对象。

  • 方法:

    • Object.assign({}, originalObj)
    • 扩展运算符 { ...originalObj }
    • Array.prototype.slice() 或 [...originalArr] (用于数组)
  • 示例:

    javascript 复制代码
    const original = {
      a: 1,
      b: { c: 2 } // b 是一个引用类型
    };
    const shallowCopy = { ...original };
    
    shallowCopy.a = 100; // 修改值类型属性,不影响原始对象
    shallowCopy.b.c = 200; // 修改引用类型属性的内部值,会影响原始对象
    
    console.log(original.a);   // 输出: 1
    console.log(original.b.c); // 输出: 200 (被影响了!)

2. 深拷贝 (Deep Copy)

  • 特点:创建一个全新的、完全独立的对象,递归地复制原始对象及其所有嵌套的引用类型数据。

  • 方法:

    • JSON.parse(JSON.stringify(originalObj)) (简单但有局限性,不支持函数、undefined、Date 等)
    • 使用库函数,如 Lodash 的 _.cloneDeep()
  • 手动实现递归拷贝函数

  • 示例:

    javascript 复制代码
    const original = {
      a: 1,
      b: { c: 2 }
    };
    
    const deepCopy = JSON.parse(JSON.stringify(original));
    
    deepCopy.b.c = 200; // 修改深拷贝对象内部的嵌套对象
    
    console.log(original.b.c); // 输出: 2 (原始对象不受影响)
    console.log(deepCopy.b.c); // 输出: 200

总结:

场景 操作 结果 说明
值类型赋值 let x = 5; let y = x; y 是 x 的值的副本。 修改 y 不会影响 x
引用类型赋值 let a = {x: 5}; let b = a; b 和 a 指向同一个对象。 通过 b 修改对象属性会影响 a
重新赋值 let a = {x: 5}; let b = a; b = {x: 10}; b 现在指向一个新对象。 a 仍然指向原来的对象,不受影响
浅拷贝 let copy = { ...original }; 创建新对象,但其引用类型属性仍指向原对象。 只深拷贝了一层
深拷贝 let deepCopy = JSON.parse(JSON.stringify(original)); 创建一个完全独立的新对象,包括所有嵌套数据。 两个对象彻底无关
相关推荐
努力学习的小廉1 小时前
我爱学算法之—— BFS之最短路径问题
算法·宽度优先
高山上有一只小老虎2 小时前
构造A+B
java·算法
木头左2 小时前
缺失值插补策略比较线性回归vs.相邻填充在LSTM输入层的性能差异分析
算法·线性回归·lstm
sin_hielo2 小时前
leetcode 2435
数据结构·算法·leetcode
crescent_悦3 小时前
PTA L1-020 帅到没朋友 C++
数据结构·c++·算法
鳄鱼儿3 小时前
密码算法的OID查阅
算法
lxh01134 小时前
螺旋数组题解
前端·算法·js
czlczl200209254 小时前
算法:二叉树的公共祖先
算法
稚辉君.MCA_P8_Java5 小时前
Gemini永久会员 Java动态规划
java·数据结构·leetcode·排序算法·动态规划