freeCodeCamp 每日编程挑战实战解析:用 Python 解决 Missing Numbers(Challenge 34)
【免费下载链接】freeCodeCampfreeCodeCamp.org's open-source codebase and curriculum. Learn math, programming, and computer science for free.项目地址: https://gitcode.com/GitHub_Trending/fr/freeCodeCamp
本篇技术指南围绕 freeCodeCamp 开源仓库中每日编程挑战(Daily Coding Challenge)系列的第 34 题 Missing Numbers 展开,完整还原题目约束、测试用例与官方参考实现,并结合仓库源码剖析这道 Python 题目从课程文件、种子数据到 API 服务的完整落地链路。读完本文,你将掌握这道缺失数字问题的分析思路与 Python 实现,并理解这类题目在 freeCodeCamp 项目中是如何被定义、校验、存储与分发的。
挑战全景:Missing Numbers 在项目中的位置
该题目位于 Python 每日挑战块(block)daily-coding-challenges-python中,源文件为 curriculum/challenges/english/blocks/daily-coding-challenges-python/68af0687ef34c76c28ffa54f.md。题目 id 为68af0687ef34c76c28ffa54f,标题为 "Challenge 34: Missing Numbers",challengeType: 29(对应 Python 侧练习),dashedName为challenge-34。
从块配置文件 curriculum/structure/blocks/daily-coding-challenges-python.json 可以看到该块的元信息:
isUpcomingChange: true:标记为新体系中的变更内容;usesMultifileEditor: true:练习在多功能编辑器中完成;helpCategory: "Python":帮助分类为 Python;blockLayout: "legacy-challenge-list":采用旧式挑战列表布局;challengeOrder:包含 200+ 道按日编号的挑战,Challenge 34 只是其中一环,前后分别是 "Challenge 33: Screen Time" 与 "Challenge 35: Word Frequency"。
值得注意的一点是:同一道题存在 JavaScript 与 Python 两个独立版本。Python 版要求实现find_missing_numbers(arr),而 JS 版对应文件 curriculum/challenges/english/blocks/daily-coding-challenges-javascript/68af0687ef34c76c28ffa54f.md(challengeType: 28)要求实现findMissingNumbers(arr)。两个版本题目描述与 6 组测试用例完全一致,只是语言 API 不同——这为后面介绍的“双语言配对入库”机制埋下伏笔。
题目要求详解
原题描述非常精炼,核心是一句话:
给定一个包含从
1到n(含端点)整数的数组,返回所有介于1与n之间缺失的整数,其中n是给定数组中的最大值。
同时附带三条约束:
- 输入数组可能未排序,且可能包含重复元素——意味着不能依赖数组顺序,也不能假定元素唯一;
- 返回数组必须按升序排列——例如
[2, 4]而不是[4, 2]; - 如果没有缺失整数,返回空数组——即数组恰好完整覆盖了
1..n。
题目没有显式声明空输入的处理,但官方参考实现对空数组做了兜底(返回[]),这也是判题时应当考虑到的健壮性边界。
边界语义澄清
n由输入数组的最大值决定,而不是由数组长度决定。例如[10, 1, 10, 1, 10, 1]的n = 10,因此要检查的完整区间是1..10,而不是1..6。- 重复元素不影响结果:查找区间内某个整数“是否缺失”只看它是否至少出现一次,出现多次与出现一次等价。
- 数组未排序:实现时不能使用“相邻元素差 1”这类依赖顺序的快捷方式,需要先建立全局视角(如集合)。
测试用例逐条解析
原文档通过 6 组hints定义了完整的行为契约。Python 版采用runPython机制,在测试环境内执行unittest.TestCase().assertEqual(...)来断言结果,覆盖了典型场景、边界场景与极端场景:
| 输入 | 期望输出 | 覆盖点 |
|---|---|---|
[1, 3, 5] | [2, 4] | 基本缺失场景,中间连续缺两个数 |
[1, 2, 3, 4, 5] | [] | 无缺失,完整区间 |
[1, 10] | [2, 3, 4, 5, 6, 7, 8, 9] | 大区间、端点仅有首尾 |
[10, 1, 10, 1, 10, 1] | [2, 3, 4, 5, 6, 7, 8, 9] | 未排序 + 重复元素,且n为最大值 10 |
[3, 1, 4, 1, 5, 9] | [2, 6, 7, 8] | 未排序 + 重复,缺失分散在中间与尾部 |
[1, 2, 3, 4, 5, 7, 8, 9, 10, 12, 6, 8, 9, 3, 2, 10, 7, 4] | [11] | 长数组 + 大量重复,仅缺一个数 |
这 6 组用例合计覆盖了:升序输出、空结果、重复元素、未排序、n取最大值、单点缺失等全部语义。测试文件中的断言写法可直接用于本地验证,例如:
from unittest import TestCase TestCase().assertEqual(find_missing_numbers([1, 3, 5]), [2, 4]) TestCase().assertEqual(find_missing_numbers([1, 2, 3, 4, 5]), [])参考实现逐步拆解
题目的种子代码(--seed-contents--)给出如下函数骨架,要求补全逻辑:
def find_missing_numbers(arr): return arr官方参考解法则给出了完整实现:
def find_missing_numbers(arr): if not arr: return [] n = max(arr) seen = set(arr) result = [i for i in range(1, n + 1) if i not in seen] return result逐行分析:
- 空数组兜底:
if not arr: return []。虽然题目未显式说明,但官方解法对空输入返回空列表,避免后续max(arr)抛ValueError。 - 确定上界:
n = max(arr)。max遍历一次数组找到最大值,这就是区间1..n的上界,直接对应题目中“n是给定数组最大值”的定义。 - 去重建集:
seen = set(arr)。把整个数组转成集合,一次性消化“未排序 + 重复元素”两条约束,使后续查询降为 O(1)。 - 遍历区间筛缺失:
result = [i for i in range(1, n + 1) if i not in seen]。从 1 递增到n生成完整区间,只保留不在集合中的数。由于range天然升序,输出的result自动满足“升序排列”要求,无需额外排序。 - 返回结果:无缺失时列表推导自然得到空列表
[],与第三条约束吻合。
复杂度分析
- 时间复杂度 O(n):
max一次 O(n),建set一次 O(n),列表推导遍历1..n共 O(n)(最坏情况下n与输入规模同量级)。整体线性。 - 空间复杂度 O(n):
seen集合至多容纳n个不同元素,result最坏也接近n个元素。
作为对比,若不使用集合而用if i not in arr逐次线性查找,时间复杂度会退化为 O(n²);若先把数组去重排序再逐个比对,代码会明显更长且同样需要额外空间。官方实现是“语义清晰 + 线性效率”的平衡解。
可选变体
- 布尔标记数组:若
n有限且可接受 O(n) 空间,可用[False] * (n + 1)标记出现过的数字,再收集未标记下标; - 原地哈希 / 符号标记:利用下标与值域的对应关系在输入数组上打标记,可把空间降到 O(1),但会修改输入,且 Python 实现可读性不如集合方案。
仓库源码佐证:从课程 Markdown 到线上服务的完整链路
这道题并非孤立文本,它在 freeCodeCamp 仓库中形成了一条可追溯的数据链路,理解它有助于把“做题”与“平台实现”连成整体。
双语言配对与种子数据
从 tools/daily-challenges/seed-daily-challenges.ts 的注释与 tools/daily-challenges/helpers.ts 的fetchChallenges实现可以看到,种子脚本通过 GraphQL 从dev-playgroundsuperblock 中按daily-coding-challenges-javascript/daily-coding-challenges-python两个块分别拉取挑战,再调用combineChallenges把同一挑战号的 JS 与 Python 版本配对合并:标题、描述、测试数量必须一致,否则抛出异常(helpers.ts中显式校验jsTitle !== pyTitle、jsDescription !== pyDescription、jsTests.length !== pyTests.length)。合并后的文档包含challengeNumber、title、date、description以及javascript/python两个语言的tests与challengeFiles,写入 MongoDB 的dailyCodingChallenges集合。
API 分发
api/src/daily-coding-challenge/routes/daily-coding-challenge.ts 提供了若干公开 GET 路由,用于按日期/天/月/全部/最新查询挑战:
/daily-coding-challenge/today:返回当天(美国中部时区)挑战;/daily-coding-challenge/date/:date:按YYYY-MM-DD查询,非法格式返回 400,未来或不存在返回 404;/daily-coding-challenge/day/:day:按MM-DD查询;/daily-coding-challenge/month/:month:按YYYY-MM返回当月列表(仅含 id、challengeNumber、date、title);/daily-coding-challenge/all与/daily-coding-challenge/newest:分别返回全部已发布挑战与最新挑战日期。
该模块的说明见 api/src/daily-coding-challenge/README.md,其中明确“每日挑战的提交仍走主 API 的 challenge 提交路由”,即这些端点只负责取题信息。
客户端校验与渲染
client/src/utils/daily-coding-challenge-validator.ts 用 Joi 定义了DailyCodingChallengeFromDb的校验结构:id、challengeNumber(整数且 ≥1)、title、date、description,以及javascript、python两个语言对象,各自包含tests(text+testString)与challengeFiles(fileKey+contents)。Challenge 34 的 Python 版tests中testString正是我们在文中看到的runPython断言代码,challengeFiles则对应种子里的find_missing_numbers骨架。
由此可以确认:本文所讲题目的描述、测试与初始代码,最终会经由种子脚本进入数据库,再通过上述 API 下发到客户端渲染并判题——一条从curriculum/challenges/english/blocks/daily-coding-challenges-python/68af0687ef34c76c28ffa54f.md出发、贯穿tools/daily-challenges/与api/src/daily-coding-challenge/的完整链路。
本地验证与运行建议
若想在本地验证这道题的实现,可以按以下方式操作(仓库只读,仅介绍查看与运行方式):
- 静态对照:阅读 Python 版题目 与 JS 版题目 的
--solutions--段,对比两种语言实现思路; - 本地执行测试:将 6 组
hints中的断言整理为 Python 脚本,直接运行python3验证你的find_missing_numbers实现; - 体验完整链路:若要实际看到题目的线上形态,可参照 tools/daily-challenges/seed-daily-challenges.ts 运行种子脚本(需先启动 client 的 GraphQL 端点与 MongoDB),再启动 API 后通过
/daily-coding-challenge/today等端点观察下发数据;注意种子脚本假定客户端运行在http://localhost:8000/___graphql,且按日期调度,Challenge 34 只在对应日期才会作为“今日挑战”出现。
小结
Missing Numbers 是一道“描述极简、边界清晰”的数组题:以最大值为区间上界,用集合消化未排序与重复两条约束,再用升序遍历收集缺失项,即可同时满足输出的升序要求与空结果约定。官方参考实现以 O(n) 时间、O(n) 空间的集合方案给出简洁答案,而仓库中与其配套的双语言挑战文件、种子配对脚本、API 分发路由与客户端校验器,共同构成了 freeCodeCamp 每日编程挑战从题目定义到线上交付的完整工程化闭环。掌握这道题,既是一次 Python 数组/集合基本功的巩固,也是一次理解开源平台“课程内容如何变成线上练习”的绝佳入口。
【免费下载链接】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),仅供参考