尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

freeCodeCamp 课程源码解读:用 Markdown 挑战文件实现 Insertion Sort(插入排序)

freeCodeCamp 课程源码解读:用 Markdown 挑战文件实现 Insertion Sort(插入排序) freeCodeCamp 课程源码解读用 Markdown 挑战文件实现 Insertion Sort插入排序【免费下载链接】freeCodeCampfreeCodeCamp.orgs open-source codebase and curriculum. Learn math, programming, and computer science for free.项目地址: https://gitcode.com/GitHub_Trending/fr/freeCodeCamp本篇基于 freeCodeCamp 课程仓库中 Algorithms 模块的官方挑战文件 Implement Insertion Sort完整讲解插入排序的算法思想、题目要求与测试用例并结合仓库中的挑战解析管线challenge parser、挑战类型定义与课程结构配置还原这道题从 Markdown 源文件到在线编辑器可运行挑战的完整实现链路。读完你可以掌握插入排序的逐步执行过程、官方参考解法与替代写法、以及该仓库中挑战文件的结构与校验机制。挑战文件在课程体系中的位置这道挑战位于课程的algorithms区块block中。区块结构由 algorithms.json 定义其中challengeOrder字段以挑战id和标题列出了 10 道挑战的排列顺序插入排序排在第 7 位{ isUpcomingChange: false, dashedName: algorithms, helpCategory: JavaScript, challengeOrder: [ { id: a3f503de51cf954ede28891d, title: Find the Symmetric Difference }, { id: a56138aff60341a09ed6c480, title: Inventory Update }, { id: a7bf700cd123b9a54eef01d5, title: No Repeats Please }, { id: a3f503de51cfab748ff001aa, title: Pairwise }, { id: 8d5123c8c441eddfaeb5bdef, title: Implement Bubble Sort }, { id: 587d8259367417b2b2512c85, title: Implement Selection Sort }, { id: 587d8259367417b2b2512c86, title: Implement Insertion Sort }, { id: 587d825a367417b2b2512c89, title: Implement Quick Sort }, { id: 587d825c367417b2b2512c8f, title: Implement Merge Sort }, { id: 61abc7ebf3029b56226de5b6, title: Implement Binary Search } ], blockLayout: legacy-challenge-list }可以观察到该区块是先集合运算、后排序算法的编排冒泡排序Bubble Sort→ 选择排序Selection Sort→ **插入排序Insertion Sort本篇主角**→ 快排Quick Sort→ 归并排序Merge Sort→ 二分查找Binary Search难度由低到高递进。该区块还声明了blockLayout: legacy-challenge-list即使用经典挑战列表布局渲染。挑战文件结构一份 Markdown 定义了整道题挑战源文件 587d8259367417b2b2512c86.md 采用 frontmatter 约定标记# --xxx--的 Markdown 结构。其 frontmatter 只有 4 个字段却能唯一定位挑战并声明类型--- id: 587d8259367417b2b2512c86 title: Implement Insertion Sort challengeType: 1 forumTopicId: 301613 dashedName: implement-insertion-sort ---各字段含义如下idMongoDB ObjectId 风格的唯一标识与结构文件algorithms.json中challengeOrder引用的 id 一一对应同时也是文件名本身title在线课程侧边栏中显示的挑战标题challengeType: 1根据 challenge-types.ts 中的定义1对应js类型普通 JavaScript 编程题。该文件同时给出了视图类型映射viewTypes[js] classic渲染经典编辑器视图和提交方式映射submitTypes[js] tests提交时运行断言测试也就是说这道题的完成判定完全由题目内嵌的断言测试决定dashedNameURL 中的短横线命名slug符合 challenge-schema.js 中slugRE^[a-z0-9-]$的正则约束。文件正文按以下约定分区分区标记内容# --description--题目描述与要求渲染给用户看# --hints--提示文本 断言测试代码用于解题校验与卡住时的提示# --seed--/## --seed-contents--编辑器中预置的初始代码starter code# --solutions--官方参考解法隐藏仅在特定条件下对用户可见这一约定并非松散规范而是被解析管线强校验的challenge-parser 基于unified/remark构建了处理链其中validateSections插件会先验证所有分区标记再由addTests、addSeed、addSolution、addText等插件把各分区内容抽取为挑战对象字段// tools/challenge-parser/parser/index.js节选 const processor unified() .use(remark) .use(tableAndStrikeThrough) .use(directive) .use(frontmatter, [yaml]) .use(addFrontmatter) .use(validateSections) // 先校验所有 --xxx-- 分区标记 .use(replaceImports) .use(addSeed) // 提取 --seed-- 中的 starter code .use(addSolution) // 提取 --solutions-- 中的参考解法 // ... .use(addTests) // 提取 --hints-- 中的断言测试 .use(addText, [description, instructions, notes, explanation, transcript]);从源码结构看一道挑战的完整对象还需满足 challenge-schema.js 中 Joi 定义的 schematests必填、solutions为文件的数组的数组等解析产物会经过这一层结构校验后才进入课程数据。题目描述插入排序是如何工作的原题 description 部分的核心陈述已完整保留其技术内容The next sorting method well look at is insertion sort. This method works by building up a sorted array at the beginning of the list. It begins the sorted array with the first element. Then it inspects the next element and swaps it backwards into the sorted array until it is in sorted position. It continues iterating through the list and swapping new items backwards into the sorted portion until it reaches the end. This algorithm has quadratic time complexity in the average and worst cases.翻译成要点插入排序通过在列表前部维护一个已排序子数组来完成排序以第一个元素作为长度为 1 的已排序区取出下一个元素把它与已排序区从后往前逐个比较并后移直到找到它应插入的位置重复该过程直到遍历完整个数组。题目对算法复杂度的官方定性是平均与最坏情况下时间复杂度均为二次方O(n²)。这一点与同区块的冒泡排序描述for average and worst cases has quadratic time complexity一致。官方要求Instructions编写函数insertionSort接收一个整数数组返回按从小到大排序后的整数数组Write a functioninsertionSortwhich takes an array of integers as input and returns an array of these integers in sorted order from least to greatest.完整测试用例解析题目内嵌了 4 个断言# --hints--分区既是卡住时的提示也是判题的测试集合。原题包含 4 组断言下面逐一讲解其设计意图。测试 1函数存在性insertionSort should be a function. assert.isFunction(insertionSort);最基础的类型检查确保用户定义了一个可调用对象而非常量。测试 2大数组排序正确性function isSorted(a){ for(let i 0; i a.length - 1; i) if(a[i] a[i 1]) return false; return true; } assert.isTrue( isSorted( insertionSort([ 1, 4, 2, 8, 345, 123, 43, 32, 5643, 63, 123, 43, 2, 55, 1, 234, 92 ]) ) );测试先定义了一个辅助函数isSorted线性扫描相邻元素只要出现a[i] a[i1]即判定未排序。它把 17 个元素、含大量逆序对和重复值两个123、两个43、两个2、两个1的数组作为输入——重复值这一细节很关键插入排序的正确实现必须用严格大于而非作为比较条件否则等值元素的移动行为虽然不违反排序正确性但这里也提醒读者该数组同时考察了稳定性无关路径下的边界处理。测试 3成员守恒不增删元素assert.sameMembers( insertionSort([ 1, 4, 2, 8, 345, 123, 43, 32, 5643, 63, 123, 43, 2, 55, 1, 234, 92 ]), [1, 4, 2, 8, 345, 123, 43, 32, 5643, 63, 123, 43, 2, 55, 1, 234, 92] );sameMembers校验结果与输入具有完全相同的多重集multiset——排序只允许改变顺序不允许丢失或新增任何元素含重复计数。这与测试 2 组合起来恰好完整刻画了排序的数学定义有序 成员不变。测试 4小数组精确结果 禁用内置 sort// 精确结果断言 assert.deepEqual(insertionSort([5, 4, 33, 2, 8]), [2, 4, 5, 8, 33]) // 禁止使用内置 .sort() function isBuiltInSortUsed(){ let sortUsed false; const temp Array.prototype.sort; Array.prototype.sort () sortUsed true; try { insertionSort([0, 1]); } finally { Array.prototype.sort temp; } return sortUsed; } assert.isFalse(isBuiltInSortUsed());前一条用deepEqual对 5 元素小数组做精确比对后一条是这道题最有教学价值的技巧——猴子补丁monkey patching临时替换Array.prototype.sort为一个只置位标志的桩函数运行被检测函数后再finally恢复原型以此捕获偷偷调用内置排序的行为。这个 try/finally 保证即使用户函数抛错原型链也能被还原不污染后续测试。它说明课程的测试设计在防作弊上是有刻意的工程细节的。初始代码Seed与官方参考解法# --seed--分区提供给用户编辑器的起点代码只有三行function insertionSort(array) { // Only change code below this line return array; // Only change code above this line }注释限定了允许修改的区域editableRegionBoundaries机制在 schema 的fileJoi中有对应字段定义防止用户改写函数签名。原题# --solutions--分区给出的官方参考解法function insertionSort (array) { for (let currentIndex 0; currentIndex array.length; currentIndex) { let current array[currentIndex]; let j currentIndex - 1; while (j -1 array[j] current) { array[j 1] array[j]; j--; } array[j 1] current; } return array; }逐行拆解这段实现外层for循环currentIndex从0遍历到array.length - 1。循环进行到currentIndex时array[0..currentIndex-1]已经是有序的array[currentIndex]就是待插入的新元素。currentIndex 0时内层循环一次都不执行等价于以第一个元素初始化已排序区与题目描述完全吻合。let current array[currentIndex]先把待插入元素暂存。之所以要暂存是因为下面的后移操作会覆盖它自己的位置——这是插入排序先搬砖、再腾位、最后落位三步曲的第一步。while (j -1 array[j] current)从j currentIndex - 1开始向头部扫描。两个条件缺一不可j -1防止越界保证扫描不会越过数组头array[j] current只把严格大于current的元素后移。由于此处是而不是等值元素会停在原地这正是上面测试 2 中重复值能通过的原因之一也让该实现是稳定排序。array[j 1] array[j]; j--;把比current大的元素整体后移一格为current腾出空间。注意它不是交换而是整体平移所以每轮内层循环最多只需一次赋值而非三次交换的两个赋值。array[j 1] current;while退出时j指向最后一个不大于current的元素或-1因此j 1就是current的最终插入点落位完成。以测试 4 的输入[5, 4, 33, 2, 8]走一遍currentIndex取出 current已排序区变化过程本轮结束状态05无需移动[5, 4, 33, 2, 8]145 4 → 5 后移[4, 5, 33, 2, 8]2335 ≤ 33直接落位[4, 5, 33, 2, 8]3233、5、4 依次后移[2, 4, 5, 33, 8]4833 8 后移5 ≤ 8 落位[2, 4, 5, 8, 33]✓复杂度分析外层固定 n 次内层每次最多后移currentIndex个元素。最好情况输入已有序内层while每轮 0 次赋值总比较 O(n)最坏情况逆序每轮移动约 i 次总移动次数为12…(n-1) n(n-1)/2即 O(n²)——与题目平均和最坏二次方的描述严格对应。空间上只用了current、j两个变量是 O(1) 原地排序。等价的交换式写法读者也可以写成更直观的形式语义完全一致且同样能过全部测试function insertionSort(array) { for (let i 1; i array.length; i) { while (i 0 array[i - 1] array[i]) { const tmp array[i - 1]; array[i - 1] array[i]; array[i] tmp; i--; } } return array; }它用相邻交换代替整体平移每轮交换次数相同都是该元素越过的逆序元素个数只是常量开销略大。两种写法共同体现了插入排序的核心不变量每一轮外层迭代结束后前 i1 个元素构成有序前缀。挑战类型如何驱动判题行为frontmatter 中的challengeType: 1不只是元数据它决定整套运行机制。在 challenge-types.ts 中const html 0; const js 1; // ← 本挑战 const backend 2; // ... export const challengeTypes { html, js, backend, /* ... */ }; export const viewTypes { [html]: classic, [js]: classic, // → 渲染经典单文件 JS 编辑器 // ... }; export const submitTypes { [html]: tests, [js]: tests, // → 提交 运行 # --hints-- 中的断言 // ... };由此可以串起完整链路Markdown 文件描述/提示/seed/solution 四分区→challenge-parser 管线validateSections校验标记addTests/addSeed/addSolution抽取内容→Joi schema 校验tests、solutions、id、dashedName等必填约束→运行期viewTypes.js classic决定用经典编辑器加载seed代码submitTypes.js tests决定点击Run Tests时执行hints分区里那 4 组断言。同一机制也适用于同区块的 Implement Bubble Sort、Implement Quick Sort、Implement Merge Sort 等姊妹挑战——它们的测试结构isFunction→isSorted→sameMembers→ 内置 sort 检测几乎逐字复用只更换函数名与参考解法。小结从一道 O(n²) 排序题学到的三件事算法层面插入排序靠有序前缀 后移落位两步不变量工作O(1) 原地、稳定平均/最坏 O(n²)参考解法的精髓在于用一次暂存加while平移代替交换式比较以及与的选择对稳定性的影响。工程层面禁用内置.sort()的猴子补丁测试try/finally 还原原型展示了断言驱动教学场景中防作弊测试的可复制写法。课程管线层面一个仅含 5 个 frontmatter 字段的 Markdown 文件经由tools/challenge-parser的 remark 插件链与 Joi schema最终决定了编辑器视图、seed 代码区域和判题断言——理解challengeType到viewTypes/submitTypes的映射是读懂 freeCodeCamp 全部编程题挑战运行机制的钥匙。关键文件索引挑战源文件curriculum/challenges/english/blocks/algorithms/587d8259367417b2b2512c86.md区块结构与顺序curriculum/structure/blocks/algorithms.json挑战解析管线tools/challenge-parser/parser/index.js挑战对象 schemacurriculum/schema/challenge-schema.js挑战类型与视图/提交映射packages/shared/src/config/challenge-types.ts【免费下载链接】freeCodeCampfreeCodeCamp.orgs open-source codebase and curriculum. Learn math, programming, and computer science for free.项目地址: https://gitcode.com/GitHub_Trending/fr/freeCodeCamp创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表