freeCodeCamp 每日编程挑战(Python):Targeted Sum 两数之和索引查找的测试驱动实践
2026/9/10 0:59:41 网站建设 项目流程

freeCodeCamp 每日编程挑战(Python):Targeted Sum 两数之和索引查找的测试驱动实践

【免费下载链接】freeCodeCampfreeCodeCamp.org's open-source codebase and curriculum. Learn math, programming, and computer science for free.项目地址: https://gitcode.com/GitHub_Trending/fr/freeCodeCamp

本文以 freeCodeCamp 课程仓库中「每日编程挑战 Python 系列」的 Challenge 7: Targeted Sum 挑战文档为主体,完整解析这道"两数之和"变体题的题目规格、全部四个测试用例与参考解法,并结合仓库源码说明这类 Python 挑战在 freeCodeCamp 中如何被组织、测试与执行,帮助你掌握双指针遍历、哈希表优化以及测试驱动开发(TDD)在算法练习中的落地方式。

挑战定位:它在课程中的位置

这道挑战的源文件位于 Targeted Sum 挑战文档,属于daily-coding-challenges-python区块。从区块配置 daily-coding-challenges-python.json 可以看到:

  • helpCategoryPythonusesMultifileEditortrue,说明该区块内所有挑战均在多文件 Python 编辑器中完成;
  • challengeOrder中明确列出了本题的排序位置:"id": "681cb1b0dab50c87ddb2e518"对应"title": "Challenge 7: Targeted Sum",即它是该系列 7 号挑战;
  • 文档头部的 frontmatter 中challengeType: 29标识其为 Python 类型挑战。这一点与客户端源码互相印证:在 show-daily-coding-challenge.tsx 中,javascript分支设置challengeType: 28python分支设置challengeType: 29,且 Python 挑战文件以main.py为文件名、mainpy为文件键。

因此,本文讨论的这道题是 freeCodeCamp「每日编程挑战」Python 版的一个标准单元:一段 Markdown 源文件承载题目描述、测试(hints)与参考解答,由课程构建管线解析后驱动在线编辑器运行。

题目规格:完整继承原文档

原文档给出的题目定义如下(英文原文照录,便于对照仓库源文件):

Given an array of numbers and an integer target, find two unique numbers in the array that add up to the target value. Return an array with the indices of those two numbers, or"Target not found"if no two numbers sum up to the target.

  • The returned array should have the indices in ascending order.

即:给定一个数字数组和一个整数target,在数组中找出两个不同的数,使它们的和恰好等于target。找到后返回这两个数在数组中的下标数组;若不存在这样的组合,返回字符串"Target not found"。额外的约束是:返回的下标必须升序排列

拆解这条规格,有三个容易踩坑的细节:

  1. 返回的是下标而不是数值。这是经典 "Two Sum" 题的形态,但本变体明确要求返回索引,且需升序;
  2. "两个 unique numbers"指的是两个位置上的元素(即不能用同一个下标两次),实现时第二个循环必须从i + 1开始;
  3. 无解时的返回值是字符串"Target not found",而不是None或空列表,测试会做精确的字符串相等比较。

测试用例:四条断言构成验收标准

原文档在# --hints--段落中给出四个测试用例,每一条都以runPython包裹一段 Python 测试代码,核心是用unittest.TestCaseassertEqual做精确匹配:

用例输入期望输出考察点
1find_target([2, 7, 11, 15], 9)[0, 1]命中前两个元素(2 + 7 = 9)
2find_target([3, 2, 4, 5], 6)[1, 2]命中中间元素(2 + 4 = 6)
3find_target([1, 3, 5, 6, 7, 8], 15)[4, 5]命中末尾元素(7 + 8 = 15)
4find_target([1, 3, 5, 7], 14)"Target not found"无解分支(奇数数组中任意两数之和不为偶数 14)

用例在源文件中的原始写法形如(以用例 1 为例):

({test: () => { runPython(` from unittest import TestCase TestCase().assertEqual(find_target([2, 7, 11, 15], 9), [0, 1])`) }})

这条测试链路的执行机制在仓库源码中可以追踪到:runPython是挑战运行时注入的全局函数,最终由 python-worker-handler.ts 创建 Web Worker(加载python-worker.js)承载 Python 解释器,而 frame.ts 中通过contentDocument?.__runPython(code)把测试代码送进 iframe 执行。学员代码与find_target函数共享执行环境,因此测试可以直接调用你定义的函数。值得注意的是第四个用例中"Target not found"带引号的字符串字面量——这提醒实现者:无解时必须返回精确的字符串,多一个空格或少一对引号都会导致断言失败。

种子代码:从return arr开始

原文档# --seed--段落提供了在线编辑器的初始代码:

def find_target(arr, target): return arr

函数签名固定为find_target(arr, target),参数顺序是"数组在前、目标值在后"。种子代码return arr只是占位实现,四个测试用例会全部失败——这正是测试驱动开发的起点:先让测试红,再逐步改绿。

参考解法:双重循环的 O(n²) 实现

原文档# --solutions--段落给出的官方参考解法如下:

def find_target(arr, target): for i in range(len(arr)): for j in range(i + 1, len(arr)): if arr[i] + arr[j] == target: return [i, j] return 'Target not found'

逐行解读这段实现,可以看到它如何精确满足题目规格的每一条约束:

  • for i in range(len(arr)):外层枚举第一个元素的下标i
  • for j in range(i + 1, len(arr)):内层从i + 1开始枚举第二个下标j。这一处i + 1同时承担了两个职责——保证两个下标不同(对应"two unique numbers"),以及保证i < j,从而使返回的[i, j]天然升序,无需额外排序;
  • if arr[i] + arr[j] == target: return [i, j]:找到第一个满足条件的组合立即返回。由于外层i从小到大、内层j也从小到大扫描,返回的必然是下标字典序上"最先"命中的一对;
  • 两层循环耗尽仍未命中时,执行return 'Target not found',对应无解分支。

用四个用例手动验证:用例 1 中i=0arr[0]+arr[1] = 2+7 = 9,第一轮内层即返回[0, 1];用例 3 中i=4, j=57+8=15,返回[4, 5];用例 4 的数组全为奇数,任意两奇数之和必为偶数但枚举中无一组合等于 14,最终落入字符串分支。

该解法的时间复杂度为 O(n²)、空间复杂度为 O(1),对本题这类小数组练习完全够用,但它也引出了一个自然的进阶方向。

进阶优化:哈希表把复杂度降到 O(n)

在通过四个用例之后,可以思考:为什么需要"两两配对"地遍历?关键在于——对每个元素arr[i],我们真正需要回答的问题只是"数组里是否还存在一个值为target - arr[i]的元素"。这是一个典型的空间换时间场景:

def find_target(arr, target): seen = {} for i, value in enumerate(arr): complement = target - value if complement in seen: j = seen[complement] return [j, i] if j < i else [i, j] seen[value] = i return 'Target not found'

实现要点:

  1. seen字典记录"值 → 首次出现的下标",遍历时先查后存,保证不会把同一个下标配对两次;
  2. 命中时补数complement的下标j与当前i的大小关系不确定,因此需要min/max(或条件表达式)保证升序返回——这一点在"边扫边查"的策略下不再天然成立,是此写法相对官方双循环解法最需要小心的差异;
  3. 由于每个值只存首次出现的下标,返回结果与"扫描顺序最先命中"语义一致,对本文四个用例的期望输出不会产生分歧。

复杂度从 O(n²) 降到 O(n)(字典成员检查为摊销 O(1)),空间代价为 O(n)。需要说明的是:freeCodeCamp 的参考解法采用的是更朴素的双循环版本,教学意图在于先巩固嵌套循环与索引思维;哈希表写法属于读者自行扩展的优化路径,而非题目要求。

从 Markdown 到数据库:挑战的流转链路

从仓库结构看,这道题还有两条值得了解的工程链路,可帮助理解挑战文档是如何被使用的:

  • 构建与校验链路:挑战源文件(如 本题文档)由课程构建管线按idtitlechallengeTypedashedName等 frontmatter 字段解析,dashedName: challenge-7与区块配置中的challengeOrder条目共同决定其展示顺序;
  • 种子注入链路:每日挑战通过tools/daily-challenges工具注入数据库。按 daily-challenges README 的说明,流程是复制sample.env.env、安装依赖、以"显示即将上线变更"模式启动客户端(使脚本能从 GraphQL 拿到挑战数据),然后在该目录运行pnpm seed-daily-challenges,将挑战从"Dev Playground" superblock 写入freecodecamp数据库的DailyCodingChallenges集合。前端 show-daily-coding-challenge.tsx 再按日期从该集合拉取当日挑战并格式化为编辑器所需的 props(challengeType: 29对应 Python、challengeType: 28对应 JavaScript)。

小结

围绕 Targeted Sum 挑战文档 这条主线,本文完整覆盖了题目规格(升序索引、无解时返回"Target not found"字符串)、四个测试用例的断言细节、种子代码与官方 O(n²) 参考解法的逐行语义,并在此基础上给出了哈希表的 O(n) 优化写法及其与官方解法在"升序保证"上的差异点。同时结合 区块配置、Python Worker 实现 与种子脚本说明,展示了这道题在 freeCodeCamp 课程体系中从 Markdown 源文件、runPython测试运行时到数据库注入的完整流转方式。掌握这套"规格 → 测试 → 参考解法 → 优化"的分析路径后,你可以对同区块内的其他每日挑战(如 Challenge 6: Anagram Checker、Challenge 8: Factorializer)做同样的拆解与扩展练习。

【免费下载链接】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),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询