半夜翻抽屉找一把螺丝刀,螺丝刀没找着,倒翻出一台早就不用的随身听,里头还卡着半盒磁带。磁带被压带轮绞进去一小截,皱得像一片干掉的叶子,我找了支铅笔塞进轮孔里反着转了十几圈,也没能把它转平。那台机器上有一个专门往回走的键,按下去磁带吱吱呀呀地退,慢,可是你真的能回到刚才那一句;后来的播放器只剩一根进度条,能拖,拖不准,而且心里清楚,拖回去听到的已经不是原来那一下了。
屏幕上还开着一个东西,叫 DSA View View,跑在浏览器里的算法可视化工具,你把自己的 TypeScript 函数丢进去,它可以一步一步走给你看,数组画成柱子,矩阵画成格子,树的节点用线连起来,双指针那类题还专门留了一块地方,让水一眼看得见积在哪儿。写这个小工具的人前几天发了新的一篇,讲三道题,雨水、频率前 k 个、选择排序,我本来只想扫一眼,结果在评论区停了很久。
有一条评论说的事,跟我刚才拧那盘磁带其实是一件事。大意是,能退回循环中间某一步这个功能,对他最有用的时候,不是看不懂算法,是走神了一下,回头发现某个变量不知道什么时候变了,又不想整个从头再跑一遍。这话说到了一件很少有人肯承认的事上。
我们这一行过去这些年干的活,几乎全在往一个方向使劲,编译更快一点,反馈更短一点,最好你刚想到它已经写完了。没什么人做倒带。倒带不体面,它等于当场承认,刚才那一下我没跟上,我要回去;在人人都展示自己一次写对的地方,承认这个是要一点体力的。
走神这件事,我越来越觉得不该只算在个人头上。人的记性本来就有涨落,像潮水,装的东西多了,前面那一段就慢慢淡下去,不是忘了,是被挤到边上。机器那边也一样,context 一满,最先被挤走的往往是最早说过的那句话。既然如此,能退回去看一眼,就不该被当成笨,那是一种必要的诚实。
那篇里还有一道雨水的题,我一直觉得名字起得好。水夹在两侧最高的墙之间,最后能积住多少,由矮的那一边说了算。写的是算法,我看着那些柱子,想到的却是人跟人之间能存下多少东西,大概也是这个道理,高的那边再高也没用,得看矮的那边肯不肯往上垫一寸。
想起很多年前带过的一个学弟,不是一点就通的那种,别人两道题的时间他做一道,卡住的时候就在原地站着,一动不动。后来他那批人里他走得最稳。我问他诀窍,他说没什么,就是卡住的时候从来不肯先跳过去,非把这一步弄明白不可,哪怕今天什么都不干。我当时心里是嫌他慢的,现在回头看,那种慢是很贵的东西,因为多数人是在往前滑的,滑过去的东西不会自己回来。
评论区里还有人提到,作者从今年一月起每天的提交记录没断过。这跟倒带看着是两码事,其实是同一种东西的两头:一头是不肯断,一天一天往前连;一头是不肯混,一步不对就退回去。前者攒的是时间,后者攒的是清楚。不,这么说还是把它讲轻了,攒清楚这件事没那么功利,它更像是一种体面,人总得知道自己在哪一步上,才谈得上下一步。
底下还起了个小争论,有人问 Top K Frequent Elements 那个桶的做法是不是一定比堆好,写工具的人答得很实在,桶是线性时间,但空间按 n 开,k 小、内存紧的时候堆更划算。一来一回,把话讲明白了,没人抬杠。这种对话现在不多见,我甚至有点感动,说出来有点可笑,但我确实在半夜看两个陌生人客客气气地讨论空间复杂度,觉得今晚不算白过。
选择排序也在这一篇里。每次都从剩下的里头挑一个最小的,换到前头已经排好的那一段的尾巴上,效率是几种排序里最差的,O(n²),可它是最像人干活的一种,你看得见它在挑,看得见它在换,看得见它一步一步把左边那一截弄整齐。这种题看它跑一遍,比背十遍结论管用,尤其是隔了几年早就忘了细节的时候。
那盘磁带最后也没能转平,我把它塞回去按了播放,出来的是很闷的一声,然后就不动了。我也没再折腾它,就让它卡在那儿吧,反正我大概也想不起来当初要听的是哪一句。
天快亮了,屏幕上那个双指针还停在循环的第十七步,等我。我今晚是不会有结论了,跟那盘磁带的处境差不多,转不平,但好歹还在,没被扔进垃圾袋。这大概就是我还愿意为这么个小工具花一晚上的原因:它不替我往前跑,它只是答应我,什么时候想回去看一眼,都行。
