
题单这件事我踩了三年的坑才摸明白。2021年春天我花了一整个周末把散落在几个在线评测平台上的算法题按专题整理成一份经典题单当时特有成就感发到社群里被收藏转发了好几次。之后忙起来连续几个月没动它。直到年底有个群友私信说链接打不开了我才点开那份题单——好家伙三分之一的题目已经失效还有几道被平台下架剩下的难度排序也跟整理那天完全对不上了。从那时起我开始认真琢磨题单维护到底该怎么做。三年多过去我把这件事从想起来才做的杂活变成了一套有固定节奏、有半自动化工具支持、能持续滚动的流程。这篇文章就把整套方法完整拆开题单为什么会烂、怎么搭一个抗腐烂的结构、怎么给题目做准入评审、怎么巡检链接、怎么让用户帮你发现问题以及我实打实翻过的几次车。适合所有维护刷题社群题单、课程资料清单、面试准备题库的人思路也能平移到读书清单和教程合集上。1. 题单不是整理一次就完事它为什么会慢慢烂掉要理解维护先得理解腐烂。一份题单如果放着不管几乎一定会出现下面几种问题而且它们往往是同时发生的。1.1 链接失效与题目下架平台侧的不可控变化在线评测平台每年都会调整自己的题库。有的题目被合并进了新题有的因为内容或版权问题被整体下架还有的只是改了内部编号旧链接就变成404了。最麻烦的是这些变化发生时不会有任何人通知你。我曾经在某平台遇到过整批题库迁移原来几百道题的链接一夜之间全部跳到首页根本没有跳转规则可循。这类问题的核心特征是你没做错任何事但题单坏了。它不取决于你整理得多么仔细而是取决于外部环境一直在变。所以维护题单的第一课就是接受一个事实链接是一种保质期很短的东西必须定期复查。1.2 难度漂移与知识点重组题单还停留在整理当天比链接失效更隐蔽的问题是题目本身的难度观感会随时间漂移。我整理经典题单时会把题目标成入门、基础、进阶、挑战四个档位但难和不难从来不是题目自带的固定属性。举个很常见的例子一道题在2020年算进阶难度因为当时主流解法还没普及到了2024年新解法在社区里已经人人都会普通爱好者也能轻松写出来这道题就变得名不副实了。反之也有题目变难的情况——平台在它前面加了更前置的练习或者教材体系变了原来的入门题现在成了很多人眼中的坎。难度漂移的根源是题单的快照属性它记录的是整理当下的判断而这个判断会过期。1.3 隐性腐烂收藏数很高但没人跟着学最伤的一种腐烂是数据看不出来的。链接全部有效、题目也都还在但题单的实际使用体验已经崩了。典型情况是一道竞赛级难题被我放在新手入门区新人点进去半小时做不出来只会默默关掉页面向下放弃不会有任何人向你反馈这题太难了。他们不吐槽他们只是离开。你会发现这类问题往往藏在题单的梯度结构里相邻两道题之间的难度落差太大中间缺少台阶。所以维护不只是查链接、补链接本质上是在持续校准一张学习地图的体验质量。2. 维护的前提建立一套抗腐烂的题单结构在有工具和流程之前首先得把题单本身从一串链接升级成结构化档案。这一步决定了后面所有维护动作的效率。2.1 从一串链接到结构化档案每个条目至少六个字段如果题单只是一个链接清单维护时就只能人肉点开每个链接确认既慢又容易漏。我现在的做法是给每个题目建一行档案字段尽量标准化。你不需要一开始就搞得很复杂六个字段足够起步字段作用示例题目名快速识别最长上升子序列来源平台定位归属某在线评测平台原始编号/链接精确访问https://example.com/problem/123难度评级排序与梯度判断入门 / 基础 / 进阶 / 挑战专题标签多维度检索序列DP、二分优化当前状态生命周期管理正常 / 待验证 / 已替换 / 已删除上次检核日期追踪新鲜度2024-06-01第三点是很多人容易忽略的——状态字段。不要直接删失效的题目先标记待验证确认后再改成已替换或已删除。这样题单的变动全程可追踪半年后回头看也能知道当时发生了什么。2.2 用目录组织专题地图结构化档案解决了单个条目的问题接下来要解决整体怎么摆。我的习惯是三级组织专题 → 子专题 → 题目。以动态规划为例我会在主目录动态规划下面分线性DP、背包DP、区间DP、状态压缩DP几个子专题每一层的题目清单单独维护。这样做的好处很直接新增题目时有明确去处不用纠结塞哪月度review时能快速发现某个子专题是不是已经堆了太多重复考点的题或者某个子专题题目太少、明显偏科。2.3 为什么这些结构能降低维护成本有人觉得搞这些字段很繁琐不就是个清单吗。但你一旦开始维护三个月以上就会体会到结构化带来的复利。举两个实际场景做全量链接检查时脚本只需要读链接和状态两列其它字段完全不影响做月度review时按检核日期排序就知道哪些题超过三个月没验过优先处理它们。更重要的是结构化让替换变成可追踪的操作——旧题状态改为已替换新题入库下次review时能看到替换原因不用靠记忆猜。维护的本质是持续管理变化而管理变化的前提是给每个变化留下痕迹。3. 准入评审与定期复核一道题凭什么留在经典名单里经典题单四个字里最有分量的其实是经典。什么样的题配叫经典我给自己定了一套准入标准每道题进题单之前都要过六关每季度复核一遍也要拿这六关重新审一次。3.1 准入六问我用这六个问题筛掉八成候选题考点覆盖这题是否覆盖了本专题最核心的考察点有些题虽然经典但考得很偏进题单只会干扰主线。难度匹配难度是否与所在梯度匹配题单里有没有比它稍微简单和稍微难一点的题形成衔接题解资源网上是否有足够质量的和讨论新手卡住时搜得到帮助吗一道题如果全网只有两三篇残缺题解我会直接跳过。数据规模数据规模设计是否合理范围太小导致暴力也能过正解学了没成就感范围太大又容易让基础薄弱的用户心态崩溃。替代品同一考点有没有更优的题比的是题目描述是否清楚、平台是否稳定、题解质量是否更高。时间检验这个题是不是稳定存在超过一年了新题我倾向于等它跑一段时间再入库避开上线后频繁修修补补的阶段。这套标准最大的作用是帮我拒绝堆题冲动。以前我整理题单时总想把所有好题都塞进去结果题单膨胀到几百道根本练不完。有了六问之后一张专题地图的容量被控制在30题左右每一道都确有其位。3.2 难度梯度设计不是把难题堆在一起就叫题单准入标准是单个题的筛选梯度设计解决的是一批题的配比。我实测下来最稳的比例是金字塔形入门40%、进阶40%、挑战20%。为什么是这个比例入门题提供快速正反馈让人在第一周就能连续做出题建立我能学下去的信心进阶题是主力训练区间留出足够多的量让人反复练挑战题只放少量用来提供方向和激励而不是占用大量时间。比例不是拍脑袋定的我观察过社群里的完课率——入门题占比低于三成时新人的七天留存率明显下降因为很多人卡在第五六题就放弃了。3.3 定期复核的淘汰标准什么时候该降级、替换、删除复核不能只查链接更要重新评估这道题还配不配待在这个位置。我常用的处理规则链接失效且找不到迁移入口 → 直接删除不留坟头。平台下架但找到了可靠的等价题目 → 新题顶替旧题进退役名单。同一考点出现了更优题目 → 用新换旧把旧题的已替换备注里写上新题号。难度评级和多数用户的实际体验严重不符 → 降级或升级而不是硬留。每隔一个季度我会把所有退役名单和待验证条目整体过一遍该归档的归档该恢复的恢复。这个环节其实就是给题单做一次季度体检让每个条目都有明确的去处而不是让旧信息无限堆积。4. 巡检实操链接检测、状态追踪与自动化辅助有了结构化题单接下来就是怎么把维护从一个模糊说法变成具体动作。这部分我按三个时间尺度来安排每周、每月、每季度。4.1 巡检节奏怎么定周检、月检、季检各管什么每周快速巡检15分钟只看状态字段异常和本周新增的用户反馈顺手处理。每月全量链接检查跑脚本扫一遍全部链接标记失活项再人工核验。每季度内容review把每个专题从头到尾过一遍结合六问做增减和难度调整。这三个尺度各有分工周检管响应的及时性月检管链接的存活率季检管内容的合理性。只做季检会发现反应太慢只做周检又会陷入琐碎细节三个尺度配合才完整。4.2 半自动链接检查用Python快速揪出死链刚起步时我是手工点链接几百道题点下来手指都要断了。后来写了个简单的Python脚本从CSV里读档案逐条检查URL状态。代码不复杂核心逻辑就两块先用HEAD请求看状态码被限制时再退化成GET只读响应头减少不必要的下载流量。import csv import requests def check_url(url, timeout10): headers {User-Agent: Mozilla/5.0} try: resp requests.head(url, allow_redirectsTrue, timeouttimeout, headersheaders) if resp.status_code 400: return ok # 部分平台不支持HEAD请求退回GET只读响应头 if resp.status_code in (403, 405, 501): resp requests.get(url, streamTrue, timeouttimeout, headersheaders) if resp.status_code 400: return ok return dead except requests.RequestException: return dead def main(csv_path): with open(csv_path, newline, encodingutf-8-sig) as f: reader csv.DictReader(f) for row in reader: if row.get(链接): status check_url(row[链接]) print(f{row.get(题目名, 未知)} | {status} | {row[链接]}) if __name__ __main__: main(problems.csv)脚本输出每一行的状态我拿结果文件按失活条目排序就行。唯一需要人工介入的是平台方设置了访问限制的情况——有时候链接明明正常但脚本返回403这就要点开看一眼确认。所以脚本的价值是缩小人工排查范围而不是完全替代人工。4.3 定时巡检把检查变成自动发生的事情月检最容易被这个月太忙跳过。我的解法是把检查脚本挂到定时任务里到点自动跑自动留档。如果你把题单档案放在Git仓库可以用CI的定时任务每天或每周自动执行一次脚本把结果输出成报告。核心逻辑就三件事拉取最新数据、执行检查脚本、留下本次结果。name: link-checker on: schedule: - cron: 0 9 * * 1 workflow_dispatch: jobs: check: runs-on: ubuntu-latest steps: - uses: actions/checkoutv4 - uses: actions/setup-pythonv5 with: python-version: 3.11 - run: pip install requests - run: python scripts/check_links.py dataset/problems.csv check_result.txt - uses: actions/upload-artifactv4 with: name: check-result path: check_result.txt这里的关键是让检查自动发生而不是靠你记得发生。定时任务把维护动作从我有没有空变成了系统到点就会做心理负担小很多。4.4 巡检之后的动作标记、替换、留痕脚本跑完不代表巡检结束更重要的是后续处理动作。我的流程固定是三步先把失活的条目标记成待验证这个动作在档案里只要改一个字段。人工点开每个失活链接看一遍。有的只是临时抽风过两天自己恢复确认真失效的才进入下一步。确认真失效后做替换或删除并在备注里写清楚操作日期和原因。最后这步留痕容易被忽略但它决定了半年后你回头看档案时能不能还原当时的情况。没有备注的删除等于给自己埋了一个历史谜题。5. 社区反馈驱动的动态优化用户吐槽是最值钱的维护信号链接检查和内容review是在内部主动发现问题但题单真正的使用反馈来自外部用户。这部分的价值被很多人严重低估。5.1 反馈渠道要低门槛别让用户费劲才能告诉你问题用户不会专门为你说一句第十三题链接挂了他们只会默默流失。所以反馈入口必须放在题单的可见位置并且让反馈成本降到最低在题单说明里写一行发现题有问题直接回复/私信/提Issue我会在X天内处理。我做过一个小实验把反馈提示从请反馈问题改成这题有错点这里说一声我最快当天就修反馈量增加了好几倍。用户愿意帮你发现错误但前提是你把求助路径摆在他们面前。5.2 反馈分类与处理优先级不是每条反馈都同等紧急收到的反馈五花八门我习惯先分类再定优先级反馈类型处理时限处理方式链接失效1天内标记待验证人工确认后替换或删除难度标注不准1周内拿六问复核必要时调级题目顺序不合理季度review记录需求池季检时统一调整希望新增某考点季度review计入需求池评估是否值得扩题题解体验差长期更新说明或在题单备注里给出替代思路分类最大的价值是让你不用每条反馈都立刻响应。链接挂了是紧急问题一天内必须处理这题很无聊可以慢慢消化。不被所有反馈牵着走才能长期保持维护节奏。5.3 版本节奏小修随时做大改按季度来给题单记版本号是个好习惯但不用搞复杂语义化用2024.06-1这种格式就够了年份月份加修订序号。小修比如挂了个链接顺手做改完在版本记录里添一笔季度大review时把需求池和退役名单整体过一遍决定下个版本的内容和结构调整。版本号的存在还有一层作用它让使用者知道题单是活着的东西。看到最近更新2024年9月的人比看到上次更新2022年1月的人更敢放心用。6. 踩坑记录我在维护题单时翻过的几次车前面讲的都是方法最后讲几个真实的失败案例。这些坑我全部踩过每一个都让题单付出过代价。6.1 批量改题号导致全线错乱有一次我为了提高链接的可读性批量把所有链接里的参数清理了一遍。当时以为那些参数只是追踪用的删掉不影响访问。结果某平台的题号藏在URL路径的一个看起来像参数的字段里我当成垃圾信息一起删了。改完以后大量链接跳到了错误题目比404还麻烦——至少404一眼能看出来跳错题你要点进去才发现不对。那次花了整整一个晚上才把链接全部恢复。教训是批量操作之前先抽样验证两三个例子确认理解完全正确再动手批量操作之后立刻跑一轮链接检查脚本做回归。6.2 多人协同用在线表格互相覆盖后来社群里有两个人帮我一起维护题单我们共用一个在线表格。有次两个人同时编辑一个人改状态字段另一个人改检核日期结果整个表格的状态列被覆盖部分行的备注也丢了。在线表格的实时同步在这种场景下反而成了破坏工具——每个人都以为自己在改最新版。现在的做法是多人协作时用Git仓库管理文本格式的档案或者退一步规定同一时间只允许一个人改状态字段。工具本身没有好坏关键是要有明确的写权限边界。6.3 没有版本备份一次误删整组找不回最疼的一次是季度review时我拿一个排序工具处理数据结果范围选择出错直接删掉了一整个树状数组专题的18道题。因为当时根本没有备份概念只能凭记忆重建最后也只找回大概三分之二。从那以后我给自己定了一条铁律任何批量操作之前先导出一份完整快照文件名带日期比如problems_20240601.csv。这条习惯救过我很多次成本几乎为零收益却不可估量。6.4 平台改版链接迁移要主动盯不能被动等某次一个平台升级了新域名旧链接有长达半年的跳转期。我当时没有留意到公告半年后跳转期结束所有旧链接一夜之间失效。因为中间没做巡检等发现时已经全线崩盘只能逐个重新找新链接。这个教训让我意识到链接存活不只是定期查一下还得主动关注重点平台的变化。看到改版公告的第一时间去检查自己的题单比事后补救省力得多。现在这份题单我已经维护三年多了。三年里它从一份简单的链接清单长成了一棵有结构、有状态、有日志的知识树。中间换过好几轮题目有的退役、有的晋升、有的改了难度。比起做题本身维护题单更像是管理一个小型系统输入是题目和用户反馈输出是一条稳定可用的学习路径过程中最重要的是持续校正。最后分享一个对我帮助最大的习惯每天只花10分钟睡前扫一眼当天的反馈列表和状态异常标记顺手记两笔。高频小修比季度大扫除有效得多也不容易产生心理负担。希望这篇能帮你少踩一些我踩过的坑。