我以前记刷题,第一行总是“今天做了几道”。数字看起来很有秩序,像一张不断变长的进度条。做得多的那天心情很好,做得少的那天就觉得欠了什么。后来整理旧记录时才发现,题号增加得很快,能重新写出来的题却没有增加多少。
熟悉的解法一旦换了输入,原来的记忆就失效了。看题解时觉得每一步都顺理成章,关掉页面以后,连循环应该从哪里开始都要重新猜。那段时间我把题目数量放到一边,给每道题留下几条更具体的记录:我第一次想到的办法是什么,哪一个边界没有处理,为什么最后选了这个数据结构,下次遇到什么变化仍然可以沿用。
我给自己留了一份很小的复现模板。先写题意中需要留下的条件,再画出输入和输出的最短例子。代码第一次通过以后,不马上打开下一题,而是换掉一个边界:空数组、重复元素、极端长度,或者把题目中的“恰好”改成“最多”。如果程序在这些地方失败,说明我只是碰巧撞上了样例。
有一道双指针题,我看过很多次。第一次写时,左右指针的移动条件混在一起,样例能过,稍微改一下高度就错。后来我把不变量写在纸上:每次移动以后,已经处理过的区间不再需要回头;当前答案只和两侧较小的边界有关。代码没有因此变长,理解却终于有了落脚点。
复盘也不必写成漂亮的总结。我现在更愿意留下失败代码和一两句原因。比如“把排序当成了去重”“把循环终止条件写成了经验”“看到熟悉的关键词就提前套模板”。这些句子不够体面,却能在下一次遇到相似问题时把我拉回现场。
刷题给我的收获,慢慢从“做过多少”变成了“能不能把一次过程重新走一遍”。这件事和维护服务很像。一个系统今天能跑,不等于明天出了问题还能定位;一段代码今天通过,也不等于换一个条件仍然可解释。可复现的练习让能力从记忆里搬到流程里,流程留下以后,才有机会变成自己的东西。
我还在做题,也还会忘记。忘记没有那么可怕,麻烦的是忘记自己为什么错过。现在每隔一段时间,我会随机挑几道旧题,不看原来的答案重新写。如果写不出来,就把它放回待复习列表,顺便记录卡住的位置。进度条不再每天都向前,但回头时能看见一条更清楚的路。