从AC到工程能力:算法刷题的正确姿势与工程反哺 1. 从打卡式AC到真正理解我的刷题状态转变大概每个在算法刷题平台上泡过的人都经历过这样的夜晚写完一份代码在评测页面按下提交键屏幕上一圈圈转着等待结果过了绿色对勾弹出来整个人松一口气没过红色失败提示戳在眼前马上进入下一轮“看数据、看边界、改代码”的循环。这篇博客不打算教具体算法也不是零基础教程而是想聊聊我在某国内主流算法刷题平台上泡了大半年之后沉淀下来的真实感受——关于刷题方法、卡题心态、常见报错的排查逻辑以及刷题这份习惯最终怎样改变了我的写代码方式。如果你也正在刷题或者刚打算从零开始希望能从中读到一点有用的东西。说句实话我刚开始刷题的状态可以用四个字形容又菜又急。看到别人一周AC几十道题自己也买了小本本列了张“每天三题”的计划表。结果计划表只完整运行了两天第三天就因为一道中等难度的字符串模拟题当场破防——那题我断断续续改了一个多小时提交七八次不是答案错误就是运行时错误。后来回头看这道题根本没有多难问题出在我完全不懂得怎么“拆”题目一上来就想在脑海里把整个流程跑完自然容易漏掉各种情况。现在我越发觉得刷题的“题量”是最不值得晒的东西。某段时间我为了追连续打卡记录专门挑简单题做AC数量蹭蹭涨看起来特别努力。可是隔了两周去重做之前AC过的一道题居然连思路都想不起来更别提重新写出代码。那一刻我意识到这种刷法只是在欺骗自己。我把这个阶段的感受拆成几点可能是很多新手都会遇到的。1.1 第一周的崩溃是给谁看的第一周刷题最容易产生的情绪是自我怀疑。我见过不少人刷了两三天就放弃原因不是学不会而是“题目怎么这么难”“别人为什么都会”。实际上很多人晒出来的已完成题单里包含大量入门级题目真正硬核的难题占比很低。你看到的数字没你想象中那么恐怖。我自己第一周定了一个不太合理的目标直接从热题榜前十开始。结果三小时只做出半道另一半看题解才勉强写完。这种难度断层导致挫败感极强几乎想卸载软件。后来我换了个策略先把最基础的模拟、枚举、排序类题目刷熟把“感觉”找回来再逐步上分。这个转折比任何学习方法都重要先保证有事可做再追求有难可破。1.2 数量的错觉AC率不是成绩单有一段时间我很迷AC数觉得多一道题就多一分底气。但回头统计发现我AC的题目集中在少数几个知识点上一旦遇到图论或动态规划相关的新题基本还是两眼一黑。数量带来的多巴胺是真实的但能力增长是滞后的。我现在更愿意问自己一个问题如果把这周做过的所有题打乱顺序随机抽三道我能保证在限定时间内做出来吗如果答案是否定的说明这周的题目只是“看懂了”而不是“学会了”。“看懂”和“会写”之间隔着一整套完整的心智过程这个过程只能通过重复和输出建立靠刷数量代替不了。2. 卡题之后的三个选择死磕、题解、还是先跳过卡题是刷题里最普遍的状态也是价值最高的时刻。没有卡过题的人不会真正长进但如果每次卡题都用错误方式解决反而会埋下隐患。我在和很多人交流后发现大家面对卡题基本只有三种做法一直死磕、立刻看题解、换一道题做。这三种做法没有绝对的好坏关键在于什么时候选哪一种。2.1 死磕的边界我的二十分钟规则我给自己定过一个粗糙但有效的时间规则简单题十五分钟想不出就停手中等题二十分钟到三十分钟难题最多一小时。超过这个时间继续死磕的收益会急剧下降。原因很简单刷题的核心是训练“将陌生问题映射到已知模型”的直觉如果一开始的映射方向就是错的死磕一小时不一定能纠正方向反而是反复在错误思路里打转。这不是说死磕没用。偶尔我也会对一道题产生特别强的直觉觉得“就快想到了”这种时候适当延长二十分钟是值得的。但大多数情况下超时后最该做的是站起来走两圈或者干脆把题目放一放第二天再回来。很多次我都是当天没做出来第二天早晨洗脸时突然想到一种正确的解法。潜意识一直在后台运行这比坐在屏幕前干瞪眼要高效得多。2.2 题解的三个阅读层次看题解是高杠杆行为使用方法不同收益天差地别。最低级的用法是“找答案抄代码”这种基本等于白做因为评测系统记下的只是结果你的大脑什么都没留下。中间层是“看思路后自己写”也就是理解题解作者用了什么算法、什么数据结构然后关掉题解重新实现一遍。最高层的用法是我后来才慢慢掌握的方式先写下自己的卡点在哪里再带着卡点去看题解中“为什么能想到这一步”。举个例子一道题我卡在“为什么需要排序”于是去看题解结果发现排序并不是关键关键是把不规则区间拍平之后问题会变成另一种更熟悉的形式。我相比直接抄代码多留住了一个解题模式的抽象概念当条件存在先后依赖时先通过排序让问题具备某种单调性。这个抽象概念以后还能用在其他题目上。合上题解之后把代码重新写出来是检验理解的最低标准第二天还能不能写出来才是真正的掌握。2.3 跳题不是逃兵是策略有一部分人把“不AC不换题”当成一种荣誉我觉得大可不必。如果一道题涉及的知识点超出当前水平太多硬啃只是在消耗心态。正确的做法是先收藏等做完了相关的几道基础题再回头挑战。跳题时额外做的一步是记录“卡点关键词”。比如我会在题目列表旁边写“区间合并”“拓扑排序的变体”“需要离散化但没思路”之类的短句。下次回来时这些关键词直接告诉我上次卡在哪儿省去重新读题、重新分析的时间。我把这个习惯称为“给未来的自己留张便签”实践下来比任何错题本都管用。3. 评测机返回的不只是对错那些让我卡壳大半个晚上的报错评测平台最有价值的地方其实不是那个红红绿绿的结果而是它提供的确定性反馈。在工程代码里同样一个错误可能藏在复杂的调用链里半天也定位不到在刷题环境下错误被约束在一个函数、一段逻辑里这对培养排错能力是绝佳训练场。下面聊几个我踩得最多的坑类型和排查思路。3.1 数组开小与RE一次图论题的深夜排查有一道图论题我本地测试了好几个用例都正常提交后立刻报运行时错误。刚开始我以为是自己读入出了问题翻来覆去检查输入格式没有发现异常。折腾半个多小时之后我试着把代码里一个邻接表数组的大小改了一倍再提交过了。后来我复盘才意识到自己对“边数”的估算太想当然。题目输入描述里写的是“N个点M条边”我看成最大五万实际数据能到十万以上。更蠢的是这个报错无论怎么检查逻辑都查不出来因为问题根本不在逻辑在资源申请。从那以后我给自己定了条规矩涉及图论的题目开数组时优先按数据范围上限再加一个常数余量而不是按输入规模的某个猜测值。宁可多占用一点内存也不要去赌边界。3.2 TLE只看复杂度是不够的超时是刷题人最常见的噩梦。我遇到过最憋屈的一次算法复杂度分析没毛病但本地跑一遍只要零点几秒最后还是超时。排查到最后发现是输入输出方式的问题。当数据规模到十万甚至百万级时默认的输入流加上未关闭的同步选项开销会比快速读取大上好几倍光这一步就能让原本及格的时间比如1.5秒变成2.5秒。后来我养成了习惯只要题目里的数据规模上了十万第一件事就把输入输出切换成更快的读取写法而不是等提交之后再被评测机教做人。复杂度之外还有一个容易被忽略的点常数优化不是追求花哨是去掉多余的重复计算。有一次我写连续子数组最大和类题目手动维护了一堆前缀和数组每次查询都做两次减法思路完全正确但常数大最后还是比预期多了零点几秒。把重复计算压到最低比某些看起来高深但实际更慢的写法更实用。3.3 WA边界条件比核心逻辑更致命答案错误这个东西最折磨人的地方在于它“看起来什么都对”。我在这个问题上栽的次数最多最后总结出一套边界检查清单每次提交前按清单过一遍数据为0时程序会不会崩只输入一个元素时输出对不对所有元素都相同的情况已经是最大解/最小解的情况负数参与取模时结果符号是否符合预期数据范围接近最大值时变量类型够不够。举一个典型例子二分查找的退出条件。我写过很多次二分最常犯的错误是把循环条件写成小于号还是小于等于号搞混导致到头来搜索区间缩小过头或漏掉一个位置。现在遇到二分题我第一反应不是写代码而是先在草稿纸上把区间闭开关系画清楚再动手。边界条件这类问题靠“再仔细一点”几乎解决不了因为你越想小心越容易把注意力放在核心逻辑上而忽略边边角角。只有把“检查边界”变成一道固定流程才能真正减少WA。4. 算法刷题对写工程代码的反作用力很多人认为刷题和写工程是两条平行线一个拼算法思维一个拼架构和稳定性。我不这么看。刷题养成的习惯会实实在在地渗透到工程代码里只是有些是正向的有些是负向的得自己分辨。4.1 好的影响边界意识、复杂度直觉与调试手段第一点是边界意识。在刷题平台上被WA教育过几百次之后写工程代码时我会下意识地考虑参数为空、文件不存在、数组下标越界这类场景。这种对边界的敏感不是volunteer凭空练习出来的而是靠一行行失败代码喂出来的。第二点是复杂度直觉。做过大量题目之后再看到某个功能模块需要遍历几层循环或者要在大数据集上做频繁查找我会本能地估算一下时间量级和内存开销把可能成为性能瓶颈的地方提前改掉而不是等项目上线后被用户反馈卡顿。刷题带来的“看一眼数据规模就心里有数”的能力在工程场景里非常值钱。第三点是调试手段。刷题让我把“对拍”这个技巧掌握得特别熟写一个暴力解法作为基准再用优化解法跑同一组随机数据两边的输出不一致就说明优化思路有漏洞。这个思路迁移到工程里就是拿旧的稳定版本和新版本在相同输入下做结果对比能快速找出回归性Bug。4.2 不好的影响为优化而优化的代价刷题语境里追求时间复杂度和空间复杂度是理所应当的但这一套搬到工程里有时会变成坏习惯。我见过一些刷题很猛的人写接口代码满脑子都是位运算、滚动数组、在极端情况下省掉几个字节内存结果代码可读性极差同事接手时完全看不懂。我也有过类似阶段写完一段代码之后不写注释因为刷题时从来不需要注释觉得“我自己能看懂就行”。但工程代码是要活很多年的会被很多人阅读和维护的。现在我把这两套标准分开刷题可以追求精简工程代码优先追求清晰。变量名长一点没关系多写一行注释也不会被扣复杂度但让后来人少挠一次头发价值极大。4.3 我的时间配比与切换方式我目前保持的节奏是工作日每天最多花四十分钟刷题当作写项目代码前的思维热身周末只安排半天整块时间做项目其余时间复盘本周刷题。这个配比可以让我既保持在算法上的手感又不至于把精力全消耗在应对小规模问题上忽略了真正常见的代码组织、模块划分和可维护性训练。另外一个很实际的建议是切换心态。刷题时我是“在一个封闭世界里寻找唯一答案”写项目时我需要主动面向不确定性需求会变、依赖会升级、用户输入不可控。这两种心态不矛盾但需要刻意区分。用刷题的心态写工程代码容易把需求当成一个固定不变的条件用工程的心态刷题又容易在简单的题目上想太多。明白自己在做哪件事本身就是一种能力。5. 复盘和归档把一道题的价值榨干做完一道题并不代表这道题属于你了。如果过两周回来题目是旧题、解法是新发现那就说明之前的刷题过程只走了形式。我从一位很会刷题的朋友那里学到了“一题三记”的方法一直用到现在非常推荐。5.1 一题三记思路、复杂度、失误点所谓一题三记是在每道值得记录的题后面写下三样东西不用长篇大论几句话就行。第一句记思路最好压缩成一个带抽象层级的句子。比如“这题的核心是把无序区间通过排序变成单调序列再用双指针维护窗口”而不是“这题用了双指针”。前者记录的是一类问题的解法模型后者仅仅记录了一个标签。第二句记复杂度包括时间和空间。写这个不是为了背复杂度理论而是为了建立“看到数据范围马上知道算法级别够不够”的条件反射。一道题如果输入规模是十万你脑子里应该瞬间出现“至少做到O(n log n)O(n^2)就别想了”。第三句记失误点只写自己实际犯过的错。这是三记里最有价值的部分因为会错的地方就是思维死角。我反复出现的失误包括忘了开long long、数组下标从0还是从1没统一、字符串输入带空格时读法不对等。每次把这些写下来下次遇见同类题就会形成提醒。5.2 模板库要自己亲手重建网上有很多现成模板二叉树、并查集、最短路一抓一大把。我一开始也是直接复制到编辑器里备用后来发现真正比赛或者做限时练习时我根本不敢用这些模板因为没亲手写过不知道里面每一步在干什么改都不敢改。后来我把常用模板全部亲手重写了一遍每写一个都在本地跑几个测试用例并附上自己写的中文注释。这个过程比刷十道题还有用。模板的本质不是固定代码而是你脑中算法模型的“具象化快照”。自己重建模板等于把抽象模型变成肌肉记忆遇到变种题时才有底气调整细节。5.3 月度复盘那些反复出现的Bug不是巧合我每个月会花一点时间翻一下上个月的“失误点”记录做个简单统计。连续两个月我发现自己都在与“取模负数”较劲于是干脆把所有取模相关的问题整理成一个专项小文档负数取模的结果在多数语言里都偏向前一个数需要的时候先加模数再取模两个大数相乘可能溢出必须转成更大范围类型。这种按月回顾的方式比每天整理错题本更适合长期坚持因为它压力小而且看到“这个月比上个月少犯两次同样的错”会让人产生一种很扎实的成就感。刷题练的本来就是长期能力用月度频率来校准方向比每天焦虑今天刷了几道题要健康得多。6. 一些实在话刷题拼的不是拼命是可持续最后这部分写给刚开始刷题的人也写给那些正在被题量焦虑困住的人。我见过一天刷二十道题的猛人也见过一周只稳定刷五题的人。前者通常坚持不了一个月后者反而三个月后肉眼可见地变强了。刷题是一个长期主义游戏拼的从来不是单日爆发力。6.1 拒绝和他人比较题目数量最容易引发焦虑刷题平台上的动态和榜单很容易让人产生一种错觉别人每天都在进步自己原地踏步。但那些数字不会告诉你别人是不是已经把同类型的题做过一遍是不是基础本来就很好或者那道题本身是不是只有五分钟难度的签到题。我的建议很简单把热榜和动态关掉只和自己上个月的状态比较。如果你想知道自己到底有没有进步用我前面提到的方式做一次自测从本周做的题里随机抽几道闭卷重做。这个比看榜单上任何数字都真实也比任何人的AC数都诚实。6.2 难度要螺旋上升经典题比偏题怪题重要很多新手一上来就挑战高难度题AC了会特别兴奋但AC三题消耗的时间和精力足够吃透同主题的基础题群。我个人更欣赏“螺旋上升”的节奏先做某个知识点的入门题再做两三道经典题然后挑战一道综合题最后回到变种题丰富边界认知。偏题怪题不是不能碰而是在基础扎实之后用来拓宽视野不应该成为日常主食。6.3 如果哪天完全不想刷那就停下来刷题不是上班不需要靠全勤奖来维持意义。我经历过一段强迫自己每天打卡的时期到了后来每天早上打开编辑器都觉得像在交作业效率差到极点。停了一周之后回归状态反而明显好转思路也更清晰。所以如果你今天完全没有刷题的心情不用勉强自己。休息几天不会让你退步真正让你退步的是带着厌倦和厌恶硬撑着刷最后把整个计划彻底放弃。最后我再说一个真实感受。那天凌晨一道卡了两天的题终于AC我在笔记里写下思路合上电脑心里其实很平静不像刚开始那样会兴奋到想截图发动态。那种平静来自一种“这题已经被彻底吃透”的确定感。真正让我怀念的不是AC的那一刻而是AC前一天晚上躺在床上还在想那道题边界条件的时刻。刷题这件事最大的意义大概不是让题单上的数量变得好看而是让你慢慢养成一种看到问题就知道该用什么工具去拆解的直觉。这种直觉一旦建立就再也不会丢掉。