freeCodeCamp JavaScript 每日挑战 Challenge 27:矩阵顺时针旋转 90 度(Matrix Rotate)实现详解
【免费下载链接】freeCodeCampfreeCodeCamp.org's open-source codebase and curriculum. Learn math, programming, and computer science for free.项目地址: https://gitcode.com/GitHub_Trending/fr/freeCodeCamp
本篇以 freeCodeCamp 课程库中的 JavaScript 每日编程挑战 "Challenge 27: Matrix Rotate"(挑战定义文件)为主体,完整给出题目、测试用例与参考解法,并从源码级索引映射角度剖析"顺时针旋转 90 度"的核心公式,最后补充一个更直观的"转置 + 反转"实现思路,帮助读者掌握二维数组(矩阵)在原地索引变换下的旋转技巧。
一、题目定位与元信息
该挑战属于daily-coding-challenges-javascript代码块,是 freeCodeCamp 面向 JavaScript 的每日练习序列之一。其前置元信息(front matter)如下:
id: 68adce01c0e1144d0a90295a title: "Challenge 27: Matrix Rotate" challengeType: 28 dashedName: challenge-27其中challengeType: 28是理解这道题运行机制的关键。在共享配置 challenge-types.ts 中可以确认该数值含义:
const dailyChallengeJs = 28;由此可知:
challengeType为28(dailyChallengeJs)表示这是一个JavaScript 每日编程挑战;- 从该文件的
viewTypes映射看,dailyChallengeJs对应的视图是'classic'(经典编辑器视图),即读者会在熟悉的单文件代码编辑框中作答; - 从
submitTypes映射看,其提交方式为'tests',意味着答案会通过与题目文件# --hints--段中列出的assert测试用例逐一比对来判分。
这些机制决定了本文的验证方式:只要你的rotate函数能稳定通过全部断言,即视为完成挑战。挑战在代码块中的排序可在 块结构文件 中查到,它位于 Challenge 26(IPv4 Validator)之后、Challenge 28(Roman Numeral Parser)之前。
二、题目描述:顺时针旋转 90 度
题目原文要求:
给定一个矩阵(一个数组的数组),将矩阵顺时针旋转 90 度并返回。例如给定
[[1, 2], [3, 4]],它看起来像:
1 2 3 4 你应当返回
[[3, 1], [4, 2]],看起来像:
3 1 4 2
核心目标:输入matrix(二维数组),输出一个新矩阵,其内容是原矩阵整体顺时针旋转 90 度的结果。注意题目要求"返回"结果矩阵,而非在原数组上原地修改,因此实现时通常构造一个新的结果数组更安全。
三、测试用例(完整继承题目断言)
题目# --hints--段给出了 4 组必须通过的断言,完整列出如下:
// 1x1 矩阵:旋转后保持不变 assert.deepEqual(rotate([[1]]), [[1]]); // 2x2 矩阵 assert.deepEqual(rotate([[1, 2], [3, 4]]), [[3, 1], [4, 2]]); // 3x3 矩阵 assert.deepEqual( rotate([[1, 2, 3], [4, 5, 6], [7, 8, 9]]), [[7, 4, 1], [8, 5, 2], [9, 6, 3]] ); // 含重复值的 3x3 矩阵 assert.deepEqual( rotate([[0, 1, 0], [1, 0, 1], [0, 0, 0]]), [[0, 1, 0], [0, 0, 1], [0, 1, 0]] );这 4 组用例覆盖了典型边界与常规情形:1x1退化矩阵、2x2小矩阵、标准3x3矩阵,以及含重复值的矩阵(后者能暴露索引计算错误——一旦行列映射写错,重复值会让错误结果"看起来对",是很好的自检用例)。
四、种子代码
题目# --seed-contents--段提供了初始骨架,默认实现只是原样返回输入,需要你在其中填入真正的旋转逻辑:
function rotate(matrix) { return matrix; }五、参考解法:双循环 + 索引映射公式
题目# --solutions--段给出的标准实现如下:
function rotate(matrix) { const n = matrix.length; const result = Array.from({ length: n }, () => Array(n).fill(0)); for (let i = 0; i < n; i++) { for (let j = 0; j < n; j++) { result[j][n - 1 - i] = matrix[i][j]; } } return result; }下面逐步拆解其设计与数学依据。
5.1 构造同尺寸结果矩阵
const n = matrix.length; const result = Array.from({ length: n }, () => Array(n).fill(0));这里假设输入为正方形矩阵(n x n,题目四个用例均为方阵)。Array.from({ length: n }, () => Array(n).fill(0))生成一个n行、每行n列且初始值全为0的新二维数组。之所以用Array.from的回调方式逐行new出一个新数组,是为了避免"所有行共享同一个数组引用"的经典陷阱——直接写Array(n).fill(Array(n))会让n行指向同一个内层数组,赋值时互相覆盖。
5.2 核心映射:result[j][n - 1 - i] = matrix[i][j]
这是整个算法的灵魂,也是最容易写错的地方。设原矩阵坐标为(i, j)(第i行、第j列),顺时针旋转 90 度后,该元素落到新矩阵的第j行、第n - 1 - i列。
用3x3用例逐格验证这一公式(n = 3):
| 原坐标 (i, j) | 原值 | 新坐标 (j, n-1-i) | 新值落点 |
|---|---|---|---|
| (0,0) | 1 | (0, 2) | result[0][2] = 1 |
| (0,1) | 2 | (1, 2) | result[1][2] = 2 |
| (0,2) | 3 | (2, 2) | result[2][2] = 3 |
| (1,0) | 4 | (0, 1) | result[0][1] = 4 |
| (1,1) | 5 | (1, 1) | result[1][1] = 5 |
| (2,0) | 7 | (0, 0) | result[0][0] = 7 |
最终result第一行为[7, 4, 1],与期望输出[[7, 4, 1], [8, 5, 2], [9, 6, 3]]的首行完全一致,验证了公式正确性。
理解这个映射的几何直觉:原矩阵的最左一列(i从0到n-1、j=0)在顺时针旋转 90 度后会成为新矩阵的最上一行。以j=0代入公式得到result[0][n-1-i] = matrix[i][0]——即原第一列从"上到下"的元素,依次填到新矩阵第一行从"右到左"的位置,正是顺时针旋转的直观表现。
5.3 复杂度
该实现为双循环遍历全部n x n个元素,时间复杂度 O(n²),空间复杂度 O(n²)(新建一个结果矩阵)。对于方阵旋转而言这已是渐近最优——每个元素都必须被访问并重新放置一次。
六、另一种等价思路:转置 + 逐行反转
除直接索引映射外,顺时针旋转 90 度还可拆解为两个更易记忆的步骤:先沿主对角线转置,再对每一行做左右反转。以下实现与上述解法结果等价,可作为交叉验证:
function rotate(matrix) { const n = matrix.length; // 步骤 1:沿主对角线转置(行列互换) const transposed = matrix.map((row, i) => row.map((_, j) => matrix[j][i])); // 步骤 2:反转每一行 return transposed.map(row => row.reverse()); }用[[1, 2, 3], [4, 5, 6], [7, 8, 9]]验证:
- 转置后得到
[[1, 4, 7], [2, 5, 8], [3, 6, 9]]; - 每行反转后得到
[[7, 4, 1], [8, 5, 2], [9, 6, 3]],与期望输出完全一致。
两种写法都能通过第三节的assert.deepEqual断言,读者可按阅读习惯任选其一。
七、边界与适用前提
- 方阵假设:参考解法以
matrix.length作为统一边长n,默认输入是正方形矩阵。题目全部用例均为方阵,因此该假设成立。 - 返回值语义:题目要求"返回"新矩阵,参考解法不修改入参,符合预期;若需在原数组上原地旋转,则需额外的交换技巧,但非本题要求。
1x1退化:当n = 1时,公式result[0][0] = matrix[0][0]自然成立,无需特殊处理,这也是第一条断言rotate([[1]]) === [[1]]能被满足的原因。- 判分方式:结合 challenge-types.ts 中
dailyChallengeJs的submitTypes为'tests'可确认,作答最终由题目文件内列出的断言集合判定,因此把 4 组用例全部跑通即代表实现正确。
八、小结
本挑战要求实现一个将二维矩阵顺时针旋转 90 度并返回新矩阵的rotate函数。核心是掌握索引映射公式result[j][n - 1 - i] = matrix[i][j],理解"原最左列旋转后成为新最上行"的几何直觉;等价地也可采用"主对角线转置 + 逐行反转"的两步法。该函数属于 freeCodeCamp JavaScript 每日挑战(challengeType: 28)序列中的第 27 题,通过题目内置的assert断言即可完整验证实现的正确性。
【免费下载链接】freeCodeCampfreeCodeCamp.org's open-source codebase and curriculum. Learn math, programming, and computer science for free.项目地址: https://gitcode.com/GitHub_Trending/fr/freeCodeCamp
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考