freeCodeCamp 每日编程挑战第171题精讲:Flatten the Array 数组扁平化全解

发布时间:2026/9/11 22:45:40
freeCodeCamp 每日编程挑战第171题精讲:Flatten the Array 数组扁平化全解 freeCodeCamp 每日编程挑战第171题精讲Flatten the Array 数组扁平化全解【免费下载链接】freeCodeCampfreeCodeCamp.orgs open-source codebase and curriculum. Learn math, programming, and computer science for free.项目地址: https://gitcode.com/GitHub_Trending/fr/freeCodeCamp导读本文深度拆解 freeCodeCamp 开源课程中「每日编程挑战」JavaScript 系列第 171 题——Flatten the Array。你将理解题目的判定标准5 组测试用例、官方递归参考解法的执行机理并掌握递归、Array.isArray、concat与push的组合运用以及flat()、reduce、迭代栈等多种替代实现与各自的时间空间复杂度最终能够在本地课程环境中自行验证解法。一、挑战概览题目要求与定位这道题出现在 daily-coding-challenges-javascript 块中文件为 69738771fb5a7b8b24cca29d.md是系列 246 道题中的第 171 道。题目描述非常精炼给定一个包含嵌套数组的数组返回一个新数组将所有值展平为一维数组并保留原数组中元素的原始顺序。核心约束只有两条展平所有嵌套层级不改变元素相对顺序。这意味着题目要求的是深度扁平化deep flatten而不是只拍平一层的浅扁平化。在课程体系层面该挑战的challengeType: 28对应dailyChallengeJs类型定义于 packages/shared/src/config/challenge-types.tsconst dailyChallengeJs 28;。同系列还有 Python 版dailyChallengePy 29二者通过 dev-playground.json 挂载在同一个 superblock 下。二、判定标准5 组测试用例逐条拆解原题--hints--部分给出了 5 组测试全部使用assert.deepEqual做深度严格比较——这意味着结果数组的元素值和嵌套结构都必须与期望完全一致deepEqual会递归比较而不是只比较引用。输入期望输出考察点[1, [2, 3], 4][1, 2, 3, 4]单层嵌套与顺序保持[5, [4, [3, 2]], 1][5, 4, 3, 2, 1]两层嵌套顺序不变[A, [[[[B]]]], C][A, B, C]四层深层嵌套[[L, M, N], [O, [P, Q, [R, [S, [T, U]]]]], V, [W, [X, [Y, [Z]]]]][L, ..., Z]六层嵌套混合边界[[red, [...[purple]]], orange, [pink, [brown]]][red,blue,green,yellow,purple,orange,pink,brown]字符串混合嵌套其中第 4 组用例嵌套深度达 6 层S位于[[[[S]]]]中T、U嵌套更深是最强约束。任何只处理单层嵌套的实现如arr.flat()无参调用都会在此失败。三、初始代码与解题起点题目给出的初始代码是一个空壳函数function flatten(arr) { return arr; }它直接返回原数组显然无法通过任何一组测试。解题者需要在函数体内实现展平逻辑。值得注意的约定是函数必须返回新数组题目明确说 return a new array因此不能通过修改原数组如原地splice的方式偷懒虽然deepEqual不检查引用但保持原数组不变是题目的隐含要求。四、官方参考解法递归 concat 逐行剖析官方在--solutions--中给出的解法如下function flatten(arr) { let result []; for (const item of arr) { if (Array.isArray(item)) { result result.concat(flatten(item)); } else { result.push(item); } } return result; }逐行解读其工作原理let result [];为当前层的展平结果分配新数组。每一层递归调用都有自己的result互不干扰。for (const item of arr)使用for...of遍历当前数组的每个元素。选择for...of而非forEach是因为后续需要在函数体内重新为result赋值result result.concat(...)而forEach回调闭包内重新赋值外层变量虽然也可行但for...of语义更直接清晰。Array.isArray(item)判断这是整个算法的核心判别。Array.isArray()是唯一可靠的数组类型检测方式——它不受跨 iframe/跨 realm 的影响也不会被typeof的局限typeof [] object所误导。注意这里没有用item instanceof Array因为跨执行环境如 iframe、worker时instanceof会因原型链不同而失效。递归分支result result.concat(flatten(item))当item是数组时先递归调用flatten(item)把它整个拆平再用concat把拆平后的结果追加到result末尾。concat接受数组参数时会将数组展开逐个追加只会展开一层但由于内层已经递归拆平最终效果是元素全部平铺。这里必须用concat而不是push——push会把整个数组当作单个元素塞进去导致嵌套依旧。result.push(item)当item不是数组时数字、字符串等直接追加到结果末尾。return result;返回当前层的展平结果。递归出口自然产生——当遍历的元素全部是原始值时函数不产生新的递归调用直接返回。递归执行流程演示以第 2 组测试flatten([5, [4, [3, 2]], 1])为例看调用栈如何展开flatten([5, [4, [3, 2]], 1]) ├── item 5 → result.push(5) → [5] ├── item [4, [3, 2]] → flatten([4, [3, 2]]) │ ├── item 4 → result.push(4) → [4] │ ├── item [3, 2] → flatten([3, 2]) │ │ ├── item 3 → result.push(3) → [3] │ │ ├── item 2 → result.push(2) → [3, 2] │ │ └── return [3, 2] │ └── result.concat([3, 2]) → [4, 3, 2] │ └── return [4, 3, 2] └── result.concat([4, 3, 2]) → [5, 4, 3, 2] ├── item 1 → result.push(1) → [5, 4, 3, 2, 1] └── return [5, 4, 3, 2, 1]由于concat和push都严格按遍历顺序追加嵌套数组的元素在其原位置被展开顺序自然得到保留这正好满足题目保留原始顺序的要求。五、更多解法从一行式到迭代栈方案一原生Array.prototype.flat(Infinity)function flatten(arr) { return arr.flat(Infinity); }ES2019 引入的flat(depth)可以指定展平深度Infinity表示展平到任意深度。这是最简洁的实现也是本挑战在真实项目中生产可用的答案。不过作为训练题目手写递归的价值在于理解底层机理——flat内部本质上也是递归/迭代遍历。方案二reduce 递归function flatten(arr) { return arr.reduce( (acc, item) acc.concat(Array.isArray(item) ? flatten(item) : item), [] ); }与官方解法逻辑等价只是用reduce累积器取代了显式的result变量。注意concat对单个元素和数组参数都能正确追加。方案三迭代 显式栈避免深递归function flatten(arr) { const result []; const stack [...arr]; while (stack.length) { const item stack.pop(); if (Array.isArray(item)) { stack.push(...item); } else { result.push(item); } } return result.reverse(); }由于栈是 LIFO后进先出直接pop会颠倒顺序最后需要reverse()恢复。该方案的优势是不依赖调用栈——当数据嵌套极深如上万层时递归方案可能触发RangeError: Maximum call stack size exceeded而迭代方案不会。六、复杂度与边界分析时间复杂度每个元素都会被访问且每遇到一层嵌套就多一层递归/循环处理总代价为 O(n)其中 n 是所有层级元素总数含嵌套容器本身。空间复杂度递归方案在最坏情况下元素全部串联嵌套如[1, [2, [3, ...]]]调用栈深度等于嵌套深度加上每层临时数组空间复杂度为 O(n)深度 d ≤ 元素数 n。迭代栈方案由于不占用调用栈其辅助空间为 O(d)其中 d 是最大嵌套深度。需要注意的边界情况空数组flatten([])应返回[]——官方解法中循环不执行直接返回空数组天然正确混合类型数组数字 字符串如第 3、4、5 组测试Array.isArray判别与类型无关均能正确处理元素为null、undefined或对象字面量时Array.isArray返回false它们会被当作普通值保留下来——这符合展平数组的语义不会误删非数组值。七、这道题在 freeCodeCamp 课程体系中的位置从源码结构可以确认这道题并非孤立存在而是每日编码挑战Daily Coding Challenges体系的一部分块定义curriculum/structure/blocks/daily-coding-challenges-javascript.json 中列出了全部 246 道 JS 每日挑战Challenge 1 到 Challenge 246第 171 题 id 为69738771fb5a7b8b24cca29d标题 Challenge 171: Flatten the Array。该块设置usesMultifileEditor: true、helpCategory: JavaScript、blockLayout: legacy-challenge-list。类型常量packages/shared/src/config/challenge-types.ts 定义了getIsDailyCodingChallenge将dailyChallengeJs(28) 与dailyChallengePy(29) 归为每日挑战类型。Superblock 归属curriculum/structure/superblocks/dev-playground.json 将 JS 与 Python 两个每日挑战块归入dev-playgroundsuperblock。自动化校验curriculum/src/test/daily-challenges.test.js 中有专门的测试套件验证 JS 与 Python 每日挑战的数量一致、每个挑战的 id、标题、描述及测试用例数量完全对齐——这意味着同样的 Flatten the Array 题目在 Python 版daily-coding-challenges-python块中也存在对应的等价实现。挑战类型合法性curriculum/schema/challenge-schema.js 规定challengeType取值范围为 03328 在合法区间内。八、在本地运行与验证如果你想在本地直接验证本文的解法无需搭建完整环境在任何支持 ES2019 的 Node.js 或浏览器控制台中即可运行function flatten(arr) { let result []; for (const item of arr) { if (Array.isArray(item)) { result result.concat(flatten(item)); } else { result.push(item); } } return result; } // 对照题目的 5 组测试用例 console.log(flatten([1, [2, 3], 4])); // [1, 2, 3, 4] console.log(flatten([5, [4, [3, 2]], 1])); // [5, 4, 3, 2, 1] console.log(flatten([A, [[[[B]]]], C])); // [A, B, C] console.log(flatten([[L, M, N], [O, [P, Q, [R, [S, [T, U]]]]], V, [W, [X, [Y, [Z]]]]])); // [L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z]题目使用的判定是assert.deepEqualNode.js 的assert模块也可以用JSON.stringify(flatten(x)) JSON.stringify(expected)做等效的结构比较。九、总结Flatten the Array 是一道教科书级的递归练习题它同时考验了数组类型检测Array.isArray、顺序保持策略concat追加、递归思想自相似子问题展平数组里的数组以及对内置 API 的理解。官方解法用 10 行代码把深度展平 保序两个约束都优雅地满足了。掌握它之后flat()、flatMap()等内置方法的原理对你而言也不再是黑盒——这正是 freeCodeCamp 每日挑战系列希望通过高频小题目训练的核心能力。【免费下载链接】freeCodeCampfreeCodeCamp.orgs open-source codebase and curriculum. Learn math, programming, and computer science for free.项目地址: https://gitcode.com/GitHub_Trending/fr/freeCodeCamp创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考