1. CPH 是什么,为什么刷 LeetCode 需要它
1.1 很多刷题党都遇到过的低效场景
先聊一个很常见的场景。很多同学刷 LeetCode 时,习惯性地打开浏览器,进入 LeetCode 题目页面,读完题后在网页右侧的内嵌编辑器里写代码,然后点击 “Run” 或 “Submit”。写完一道题后,又要切回题目描述去看下一题。如果遇到带调试需求的题目,还得在代码里塞一堆print,提交前再逐行删掉。
一天刷十道题的话,这种“浏览器来回切换 + 手动拷贝测试用例 + 反复点击提交按钮”的操作,可能就要浪费掉二三十分钟。尤其在做周赛(Weekly Contest)或者每日一题时,时间本来就紧张,这种重复操作对思路的影响非常大。
这时候,CPH 这个工具就出现了。
1.2 CPH 的完整含义
CPH 的全称是Competitive Programming Helper,直译过来是“竞赛编程助手”。它是一个VS Code 扩展插件,主要作用是把在线判题平台(Online Judge)的题目加载、测试用例运行、代码提交等重复性操作,统一整合到 VS Code 编辑器里。
你可以这样理解:以前你要在浏览器和编辑器两个软件之间来回切换,装了 CPH 之后,你只需要在 VS Code 里完成以下动作:
- 输入 LeetCode 题目编号或链接,CPH 自动把题目描述、示例输入、示例输出抓取到本地。
- 在 VS Code 中编写题解代码。
- 一键运行全部或单个测试用例,直接看到输出和预期输出是否一致。
- 确认无误后,直接在 VS Code 内提交代码到 LeetCode,拿到提交结果。
换句话说,CPH 把“读题 - 编码 - 调试 - 提交”这条完整的刷题链路,全部集中到一个编辑器界面里。
1.3 它和 LeetCode 官方插件有什么区别
很多同学刚接触 CPH 时,会把它和 LeetCode 官方 VS Code 扩展搞混。
简单对比如下:
| 特性 | LeetCode 官方扩展 | CPH |
|---|---|---|
| 题目加载 | 内置题目浏览面板,支持搜索 | 通过题目编号或链接拉取题目 |
| 测试用例运行 | 支持,但通常依赖登录状态 | 支持,并且可以批量运行 |
| 自定义测试用例 | 需要手动输入 | 可以快速追加,且能保存到本地 |
| 多平台支持 | 主要支持 LeetCode | 支持 LeetCode、Codeforces、AtCoder 等多个平台 |
| 本地文件管理 | 一个题目一个文件,管理方式固定 | 可以按题库、周赛、每日一题等目录结构自由组织 |
| 提交体验 | 稳定但界面较老 | 提交后返回结果较快,适合竞赛场景 |
单看 LeetCode 刷题场景,CPH 并不一定在所有维度上都优于官方扩展,但它的优势在于更加“竞赛化”:强调快速抓题、快速测试、快速提交,操作路径极短。如果你同时也在刷 Codeforces、AtCoder 等平台,CPH 更是一把好手。
1.4 哪些人适合使用 CPH
- 正在刷 LeetCode 热门 100 题、每日一题、周赛的算法学习者。
- 希望把本地编辑器作为统一刷题入口的 VS Code 用户。
- 需要在本地保存刷题记录、题解笔记、测试用例的同学。
- 参加 OJ 竞赛,希望提高编码效率的选手。
如果你的刷题方式目前还停留在“网页编辑器 + 手动测试”阶段,这篇文章会带你完整体验一遍 CPH 的安装、配置、实战运行和排错思路。
2. 环境准备与版本说明
2.1 前置环境要求
使用 CPH 之前,你的电脑上需要准备好以下环境:
- VS Code:CPH 是基于 VS Code 扩展机制开发的,所以 VS Code 是必需环境。
- Node.js:CPH 扩展在运行时依赖 Node.js 环境执行 JavaScript 相关任务。大部分版本要求 Node.js 12 以上,建议安装最新的 LTS 版本。
- 能够正常访问 LeetCode 的网络环境:因为 CPH 需要拉取题目描述和提交代码,网络不通的情况下会失败。
- 一个 LeetCode 账号:用于提交代码。普通免费账号即可,不需要会员。
2.2 版本说明
关于版本,有一个很现实的问题需要提前说明:CPH 版本更新速度并不算快,VS Code 版本升级后偶发兼容性问题。
本文以常见环境为例进行演示:
- 操作系统:Windows 10 / 11 或 macOS 均可,Linux 也支持。
- VS Code:1.85 以上版本。
- Node.js:16 LTS 或更高。
- CPH 扩展:以 VS Code 扩展市场中显示的 latest 版本为准。
- LeetCode 网站:使用国际版
leetcode.com或中文版leetcode.cn都可以,但配置方式略有差异。
特别注意:如果你使用的是leetcode.cn(中文站),CPH 的有些版本默认是针对leetcode.com的。遇到登录或拉题失败时,需要手动修改 CPH 配置中的 URL 前缀,后面我们会详细讲。
2.3 安装 VS Code 扩展
打开 VS Code,点击左侧活动栏的扩展图标,或者使用快捷键Ctrl+Shift+X,在搜索框中输入CPH,找到名为CPH: Competitive Programming Helper的扩展,点击安装。
这个扩展的发布者通常是agrawal-d,安装量很大,不要装错成其他同名插件。
安装完成后,VS Code 右侧可能会提示你重新加载窗口,点击 Reload 即可。
2.4 检查 Node.js 环境
在 VS Code 中打开终端(Terminal),输入以下命令检查 Node.js 是否安装成功:
node -v npm -v如果显示出版本号,说明环境正常。如果没有显示,请先到 Node.js 官网下载安装 LTS 版本,安装完成后重新打开终端。
这里有一个很常见的坑:很多同学的 Node.js 是后来才安装的,VS Code 是之前打开的旧进程,导致 CPH 一直报“Node.js not found”。解决办法很简单,完全重启 VS Code 即可。
2.5 推荐的项目目录结构
CPH 拉取题目后,默认会在你的工作区生成一个.cph目录,用来存储题目元数据、测试用例和运行结果。
推荐大家为自己的刷题代码单独建一个工作目录,比如:
leetcode-workspace/ ├── .cph/ ├── problems/ │ ├── 0001_two_sum/ │ │ ├── solution.py │ │ └── problem.md │ ├── 0015_three_sum/ │ └── ... └── daily/ └── 2025-01-15_...这样做的目的是让每个题目拥有独立的目录,题解、题目描述、测试用例都放在一起,方便后续复习。
3. CPH 核心功能与原理拆解
3.1 CPH 的工作流程
要理解 CPH 的使用方法,先要看懂它的整体工作流程。
整个流程可以分为四个阶段:
- 抓取题目:CPH 通过你提供的题目链接或编号,向 LeetCode 发送请求,抓取题目标题、描述、示例输入、示例输出以及该题目的内部 ID。
- 生成本地文件:将题目信息保存到
.cph目录,同时在当前打开的文件目录下生成或关联一个代码文件。 - 运行测试:当你点击运行测试时,CPH 会读取你写的代码文件,把每个测试用例的输入作为标准输入,运行代码后获取标准输出,再与预期输出做对比。
- 提交代码:CPH 模拟浏览器提交请求,把你的代码发送到 LeetCode,并接收判定结果(通过、解答错误、超时等)。
3.2 为什么运行测试使用的是标准输入输出
很多第一次接触 CPH 的同学会有一个疑问:LeetCode 上刷题时,函数式解题写的是def twoSum(self, nums, target):,但 CPH 运行测试时好像是拿整个文件去跑,这是怎么做到的?
其实这是 CPH 使用上一个很重要的设计取向:CPH 面向的是“标准输入输出(stdin/stdout)”模式。
当你在 CPH 中运行测试时,它并不会启动 LeetCode 的判题内核,而是:
- 把你的整个代码文件作为脚本运行。
- 将测试用例中的输入数据,通过标准输入传给程序。
- 捕获程序的标准输出。
- 把输出和预期输出对比。
因此,如果你直接写 LeetCode 那种函数式代码,CPH 运行时会提示没有入口、没有输出。
解决办法有两种:
- 方式一:在同一个代码文件中,先写好算法函数,再在文件末尾追加一段读取标准输入并调用函数的主入口代码。
- 方式二:只把 CPH 当作“抓题 + 特殊场景测试”工具,核心算法用函数式写好,在主流程中手动解析输入时调用。
我这里重点推荐方式一,因为既能保留 LeetCode 提交时需要的函数原型,又能让 CPH 在本地真正跑起来。
3.3 一个最小可运行的 CPH 执行模型
假设 LeetCode 的第一题是两数之和,你希望用 Python 在 CPH 中本地运行测试,代码文件可以写成这样:
# 文件路径:leetcode-workspace/problems/0001_two_sum/solution.py from typing import List def twoSum(nums: List[int], target: int) -> List[int]: num_to_index = {} for i, num in enumerate(nums): diff = target - num if diff in num_to_index: return [num_to_index[diff], i] num_to_index[num] = i return [] if __name__ == "__main__": # 读取测试输入:第一行是数组,第二行是目标值 import sys input_data = sys.stdin.read().strip().split() if not input_data: print("[]") sys.exit(0) nums_line = input_data[0] target = int(input_data[1]) # 示例中假设输入形式为 [2,7,11,15] nums_str = nums_line.strip("[]").split(",") nums = [int(x) for x in nums_str if x != ""] result = twoSum(nums, target) print(result)在这个文件里,twoSum函数是 LeetCode 提交时使用的核心算法部分;if __name__ == "__main__"这一段则是为 CPH 本地测试添加的标准输入解析逻辑。
当你点击 CPH 的运行按钮后,CPH 会把你配置的测试用例输入,例如:
[2,7,11,15] 9监控标准输入,最终程序输出:
[0, 1]和预期输出一致,测试通过。
3.4 测试用例是如何保存的
CPH 会自动为每个题目创建一个.prob文件,存放在.cph目录下。这个文件内部保存了题目的 URL、样例输入输出、自定义测试用例,以及本次代码文件的语言类型。
所以你在使用过程中,如果发现测试用例不刷新,一般是因为.cph下的缓存文件没有正确匹配到当前打开的题目文件,这时可以手动删除.cph目录后重新拉取题目。
4. CPH 完整使用实战
4.1 创建项目并安装扩展
第一步,在本地创建一个空文件夹,命名为leetcode-workspace。
mkdir leetcode-workspace cd leetcode-workspace code .如果命令行能够成功打开 VS Code,说明你的环境变量配置正常。
在 VS Code 中安装 CPH 扩展后,左侧会多出一个 CPH 的图标(通常是一个文件名样式的小图标)。点击它,可以看到 CPH 的侧边栏面板。
4.2 配置 CPH 支持 LeetCode 中文站
国际版 LeetCode 用户一般不需要额外配置,但如果你使用的是中文站leetcode.cn,需要手动修改扩展配置。
打开 VS Code 设置,搜索cph,找到以下配置项并修改:
{ "cph.general.leetcodeUrl": "https://leetcode.cn", "cph.general.leetcodeSubmitUrl": "https://leetcode.cn/problems/$id/submit/", "cph.general.leetcodeProblemUrl": "https://leetcode.cn/problems/$slug/description/" }这里有两个核心变量:
$id:LeetCode 内部题目编号。$slug:题目的英文短名称,比如two-sum。
如果你的版本里没有这几个配置项,可以尝试直接修改 CPH 扩展包中的package.json或全局settings.json。但更稳妥的做法是选择国际版leetcode.com使用,中文站支持在部分版本中确实不稳定。
4.3 拉取题目到本地
使用 CPH 拉取题目有两种方式。
方式一:从侧边栏输入题目编号
点击 CPH 侧边栏的加号图标,输入题目编号,例如1,选择语言为Python,CPH 会自动生成题目文件夹。
方式二:从浏览器复制链接
当你打开 LeetCode 某道题的网页时,复制浏览器地址栏的 URL。回到 VS Code,按Ctrl+Shift+P打开命令面板,输入CPH: Fetch Problem From URL,粘贴链接即可。
下面我们以从链接拉取为例,操作步骤:
- 打开 LeetCode 中任意一道题目,例如两数之和。
- 复制地址栏链接
https://leetcode.com/problems/two-sum/。 - 在 VS Code 中按
Ctrl+Shift+P,输入并选择CPH: Fetch Problem From URL。 - 粘贴链接,按回车。
等待几秒钟,CPH 会生成如下内容:
- 一个保存题目描述的文件(可能是
problem.md或直接在.cph中)。 - 测试用例列表。
- 当前打开的代码文件。
4.4 编写代码并运行测试
在生成的代码文件中,我们以 Python 为例,写入算法实现,并添加标准输入解析入口。
为了演示更完整的流程,这里用一道经典题来举例:LeetCode 073 爱吃香蕉的狒狒(Koko Eating Bananas)。
这道题在 LeetCode 周赛中经常作为二分查找的典型题目出现,非常适合展示 CPH 的本地测试流程。
题目大致意思是:有N堆香蕉,第i堆中有piles[i]根香蕉,警卫会在h小时后回来。狒狒每小时最多吃一堆香蕉,如果她决定每小时吃k根香蕉,那么耗时取决于各堆香蕉数量。求满足在h小时内吃完所有香蕉的最小速度k。
先实现核心算法函数:
# 文件路径:leetcode-workspace/problems/koko-eating-bananas/solution.py from typing import List def minEatingSpeed(piles: List[int], h: int) -> int: def can_finish(speed: int) -> bool: hours = 0 for pile in piles: hours += (pile + speed - 1) // speed return hours <= h left = 1 right = max(piles) while left < right: mid = (left + right) // 2 if can_finish(mid): right = mid else: left = mid + 1 return left if __name__ == "__main__": import sys data = sys.stdin.read().strip().split("\n") if len(data) < 2: print(0) sys.exit(0) # 输入格式: # 第一行:[3,6,7,11] # 第二行:8 piles_str = data[0].strip("[]").split(",") piles = [int(x) for x in piles_str if x != ""] h = int(data[1].strip()) result = minEatingSpeed(piles, h) print(result)然后回到 CPH 侧边栏,确保测试用例已经生成。如果 CPH 自动生成的测试用例没有解析出这道题的内部输入格式,你可以手动添加自定义测试用例。
点击 “Add Test”,添加两组测试:
输入1: [3,6,7,11] 8 预期输出1: 4 输入2: [30,11,23,4,20] 5 预期输出2: 30点击运行全部测试,CPH 会显示每个测试用例的运行结果、实际输出、预期输出以及运行耗时。
如果显示:
Test #1: Passed Test #2: Passed说明本地测试全部通过。
4.5 在 VS Code 内提交代码
本地测试通过后,点击 CPH 面板上的 “Submit” 按钮,CPH 会要求你登录 LeetCode 账号。首次提交时需要授权登录,登录后 CPH 会把当前代码文件直接提交到题目对应的判题接口。
提交后,侧边栏会显示返回结果,例如:
- Accepted
- Wrong Answer
- Time Limit Exceeded
如果反馈Wrong Answer,点击详情可以看到具体失败的测试用例,再回到本地补充测试用例进行调试。
4.6 每日一题和周赛的用法
每日一题的场景非常适合 CPH。
每天早上打开 LeetCode 网站,找到今天的每日一题,复制链接后粘贴到 CPH,拉取题目,本地写题、测试、提交,整套动作从上手到写完不超过几分钟。
周赛(Weekly Contest)场景下,CPH 的处理方式稍有不同。因为周赛题目在比赛期间通常还有反爬限制,直接通过链接拉取可能会失败。推荐做法是:
- 比赛开始前,先把周赛页面打开。
- 逐题复制链接,在 VS Code 中拉取题目。
- 如果某道题拉取失败,可以先手动建一个普通 Python 文件,把题目描述复制到注释中,先写算法,等比赛结束或网络稳定后再用 CPH 关联。
4.7 使用 CPH 调试经典 Python 异常
这里额外多聊一点。用 CPH 跑题时,最常见的错误就是 Python 代码在本地运行时报TypeError或ValueError。
举个例子,很多同学在解析标准输入时,习惯直接使用:
data = sys.stdin.read().strip().split()但 LeetCode 类题目给的测试输入格式通常是:
[2, 7, 11, 15] 9直接按空格切分会把整个[2,切成一个元素,导致后面类型转换失败。好的做法是按行读取,然后手动清洗每一行格式。
所以 CPH 不仅是一个刷题提速工具,它也在倒逼你把“标准输入解析”写得足够健壮。
5. 常见问题与排查思路
5.1 常见问题对照表
| 问题现象 | 常见原因 | 解决思路 |
|---|---|---|
| 点击运行测试无反应 | Node.js 环境缺失或 VS Code 未重启 | 安装 Node.js 并完全重启 VS Code |
| 拉取题目失败 | 网络无法访问 LeetCode | 检查网络;确认使用的是 leetcode.com 还是 leetcode.cn |
| 拉取题目失败且提示 404 | 题目编号与 URL 不匹配 | 直接从浏览器复制完整题目的 URL |
| 提交代码失败 | 未登录 LeetCode 账号 | 重新点击 Submit,在弹出的页面完成登录 |
| 中文站题目无法拉取 | CPH 默认配置指向 leetcode.com | 修改cph.general.leetcodeUrl配置 |
| 运行结果提示 Wrong Answer,但本地手测通过 | 标准输入解析逻辑与测试用例格式不一致 | 打印输入数据,检查多行解析 |
.cph目录丢失或损坏 | 工作区路径变更 | 删除.cph后重新拉取题目 |
Node.js 报错TypeError: fetch is not a function | 本地 Node.js 版本过低 | 升级到 Node.js 18+ |
5.2 提交结果 Accepted 但本地报错
这类问题主要出现在本地执行入口代码不完整时。如果你在代码中写了if __name__ == "__main__"段,而这段代码在 LeetCode 提交时也会被执行,那么就要小心它不能产生多余输出。
例如前面两数之和的例子,你在本地打印了[0, 1],提交到 LeetCode 后,LeetCode 的判题系统只认twoSum函数的返回值,主入口即使存在也不会作为最终输出。但是如果你在主入口里写了print调试语句,部分 LeetCode 题型会提示输出多余内容。
最佳实践:提交用的算法代码是一份文件,本地测试的主入口尽量保证“只在有输入时输出,且不干扰函数逻辑”。
5.3 拉取题目后测试用例为空
第一次使用 CPH 拉取题目时,如果发现测试用例列表为空,最常见的原因是 LeetCode 页面结构更新后,CPH 没有识别到样例输入输出。
解决办法:
- 手动在 CPH 面板中添加测试用例。
- 更新 CPH 扩展到最新版本,或者安装社区 fork 版本。
这里也要提醒一下:CPH 本质上是一个个人维护的开源扩展,LeetCode 改版后它经常需要一段时间来适配。遇到解析不到测试用例的情况,不要死磕,手动添加测试用例是最快的路径。
5.4 每次拉题都要求重新登录
CPH 的登录状态是基于浏览器 cookie 或 token 的。如果你的网络环境切换了 IP,或者浏览器端清理了 cookie,CPH 可能失去登录态。
建议在提交前先确认一次登录状态,如果长时间不刷题,也可以在浏览器中刷新一次 LeetCode 页面,确保账号有效。
6. 最佳实践与刷题效率提升建议
6.1 代码文件与目录规范化
刷题不能只追求 “Accepted”,代码的目录管理同样重要。推荐每个题目的目录按以下结构保存:
problems/ ├── 0001_two_sum/ │ ├── solution.py │ └── notes.md ├── 073_koko_eating_bananas/ │ ├── solution.py │ └── notes.mdnotes.md里记录:
- 题目的解题思路。
- 复杂度分析。
- 关键易错点。
- 是否可以套用模板(如二分查找模板、滑动窗口模板)。
这样坚持一个月,你积累的不只是 AC 数量,而是一套可以随时检索的算法笔记库。
6.2 标准输入解析模板化
因为 CPH 本质是标准输入输出模式,建议准备一套自己的解析模板。
常见的输入格式有以下几种,每种都建议提前写好解析函数。
示例一:数组 + 目标值
import sys def parse_input(): data = sys.stdin.read().strip().split("\n") nums = eval(data[0]) target = eval(data[1]) return nums, target示例二:多组数组
import sys def parse_multiple_arrays(): data = sys.stdin.read().strip().split("\n") arrays = [] for line in data: if line.strip(): arrays.append(eval(line.strip())) return arrays示例三:第一行是测试组数,之后每组多行
import sys def parse_grouped_cases(): data = sys.stdin.read().strip().split("\n") t = int(data[0]) cases = [] idx = 1 for _ in range(t): n = int(data[idx]) arr = list(map(int, data[idx + 1].split())) cases.append((n, arr)) idx += 2 return cases注意:上面用了eval解析输入,只为本地测试方便。因为这些都是你自己本地的刷题脚本,不是生产代码,安全性问题可以接受。但如果你把刷题文件夹放到公共仓库,建议改用json.loads或手写解析函数,避免安全隐患。
6.3 合理使用自定义测试用例
CPH 的自定义测试用例功能是非常宝贵的调试工具。不要只依赖 LeetCode 给的两三个样例就急着提交。
建议每道题至少补充以下三类测试用例:
- 边界值用例:空数组、只含一个元素、最大数值、最小数值。
- 重复元素用例:数组中存在相同元素时,逻辑是否仍然正确。
- 大数量级用例:在本地用大数组测试性能,避免提交后 Time Limit Exceeded。
举个例子,如果你在刷二分查找类的题目,可以手动构造一个庞大的有序数组:
[1, 2, 3, ..., 1000000] 500000这样就能提前判断时间复杂度是否达标。
6.4 提交前的一分钟检查
使用 CPH 提交前,建议养成一个固定习惯:
- 确认当前打开的代码文件,就是侧边栏关联的题目文件。
- 确认没有多余的调试
print。 - 确认主入口不会在 LeetCode 提交环境中产生副作用。
- 确认代码语言的解释器版本与 CPH 运行配置一致。
虽然 CPH 会尽量降低误提交的概率,但快速检查一遍总比提交后后悔好。
6.5 与 Git 结合做刷题进度管理
如果你长期刷 LeetCode 热门 100 题,强烈建议在leetcode-workspace目录下初始化 Git 仓库:
git init git add . git commit -m "feat: solve two sum with hash map"后续每一次 AC,都可以按题目为单位提交。这样你就有了一份完整的历史记录,复习时可以顺着 Git 历史查看每一步思路的变化。
.gitignore中建议忽略.cph目录,因为测试用例的中间缓存文件不需要纳入版本管理:
.cph/ __pycache__/6.6 与 LeetCode 周赛结合的训练节奏
很多同学刷题不稳定,今天做几道明天就停下了。一个有效的节奏是:
- 周一至周五:每日一题 + 一道热门 100 题,总计两道。
- 周六:参加 LeetCode 周赛,前三题用 CPH 本地完成。
- 周日:复盘周赛题目,把没做出来的题补完,并写成笔记。
有了 CPH 之后,每次刷题的环境成本几乎为零,更容易坚持下来。新题到手,直接拉取、编码、验证、提交,整个过程不超过十分钟。这种流畅感对培养刷题习惯非常有帮助。
7. 总结与下一步学习方向
CPH 这个工具,本质上解决的是刷题流程中的“环境切换”和“重复劳动”问题。它不会替代你思考算法,也不会自动帮你写出正确答案,但它能把整个刷题过程变得更加连贯。尤其是当你需要长时间集中刷题时,少一次鼠标点击,就少一次注意力分散。
在本文中,我们从 CPH 的功能定位出发,搭建了完整的 VS Code 刷题环境,拆解了 CPH 基于标准输入输出的底层运行机制,并通过两数之和、爱吃香蕉的狒狒这两道题演示了从抓题、本地测试到在线提交的完整流程。同时,也整理了拉题失败、测试用例为空、Node.js 报错等高频问题的排查方法。
如果你接下来想在刷题效率上更进一步,可以考虑以下几个方向:
- 深入学习 VS Code Tasks 功能,为不同语言配置一键编译运行脚本。
- 把
.cph目录下的题目元数据与自己的笔记系统打通,形成“题目 + 题解 + 笔记”三位一体的复习库。 - 使用 Python 脚本批量导出已 AC 的题解,生成个人题解索引页,方便面试前快速翻阅。
工具永远只是起点,真正的核心还是你每天是否持续地思考、练习和总结。期望这篇文章能帮你省下那些花在重复操作上的时间,把它们真正用在算法学习和思路训练上。