周三下午,面试官在白板上写下这行字:"给定一个整数数组,判断是否存在重复元素。"
我盯着题目看了十五秒,脑子里蹦出的第一个念头是:遍历每个数字,再往后扫一遍,看有没有一样的。手写代码,两个for循环嵌套,O(n²)复杂度。这确实是人类直觉——拿起一个数,眼睛往右扫,找到一样的就喊停。
但面试官的表情告诉我,这道题的价值不在这里。
问题的关键不是"这个数后面还有没有",而是"这个数我之前见过没有"。把问法翻转过来,解法就从"扫描未来"变成了"回忆过去"。这时候你需要一个容器,记住所有已经路过的数字——哈希集合(hash set)就是干这个的。查一次O(1),加一次O(1),整个数组扫完最多O(n)。
这个思路的精妙之处在于"循环不变式":走到第i步时,集合里恰好存着从0到i-1的所有元素。如果当前数字已经在集合里,说明它是第二次出现,直接返回true;如果走完都没触发,说明全员唯一。
代码写出来很干净。TypeScript版本:建一个Set,for-of遍历,has()检查,add()收录,遇到重复立即return。Python几乎一样,set的in操作背后也是哈希。时间复杂度O(n),空间换的。
走一遍例子就懂:输入[1,2,3,1],第一步seen是空,1进去;第二步查2,不在,2进去;第三步查3,不在,3进去;第四步查1,在!返回true。全程没有回头扫,也没有重复比较。
有人可能会写一行代码解决:new Set(nums).size !== nums.length。这确实能跑,但面试官不爱看——它必须处理完整数组,哪怕重复出现在第二个位置。循环版可以提前退出,而且显式展示了"我见过你吗"这个核心模式。建议先写循环版,再提一行式作为变体。
这个模式到处都在用。两数之和(Two Sum)里,你存的是值到索引的映射;这里只需要存值本身。更复杂的场景,比如字符串去重、路径判环、缓存实现,底层都是同一套逻辑:用哈希结构回答"我见过你吗"。
那十五秒的直觉错了吗?没错,只是太慢。算法训练的本质,就是把"人眼扫描"翻译成"机器记忆"。这道题教会我的不是某个API怎么用,而是遇到"查重"类问题时,先问自己:能不能把"往后找"换成"往前记"?
热门跟贴