算法困惑问答 · LC 1 两数之和
两数之和为什么用哈希表,而不是先排序再双指针?
直接答案
因为题目要返回的是原始下标。排序会把下标打乱,排完还得额外记录「值 → 原下标」的映射才能还原答案,代码更绕、复杂度还多出 O(n log n) 的排序。哈希表方案边遍历边查「还差的另一半在不在表里」,一遍扫完 O(n) 直接拿到原下标——无序、要下标,哈希就是最短路径。
排序方案到底亏在哪
排序 + 对撞双指针本身没错,它是有序版两数之和(LC167)的标准答案:O(n log n) 时间、O(1) 额外空间。但放到 LC1 上有个硬伤——排序破坏了元素和原下标的对应关系。你找到了那两个值,还得回头查它们原来在第几位,要么排序前先把 (值, 下标) 捆在一起,要么再建一张映射表。绕了一圈,空间也没省下来。
结论性的取舍是:要「原始下标」且数组无序 → 哈希表;数组已有序、或只要「值」不要下标、或内存紧张 → 双指针。同一道题的变体考的就是这个判断。
哈希方案的机制:查在前、存在后
从左到右扫,每拿到一个数 x,先查表里有没有 target − x(它的「另一半」):有,答案就是「表里存的下标 + 当前下标」;没有,把 x 和它的下标存进表,继续往前走。
顺序必须是「先查、后存」。如果先把所有数一股脑存进表再查,当 target 恰好是某个数的两倍时(比如 target=10、x=5 只出现一次),x 会查到它自己,误报「同一个元素用了两次」。先查后存则天然保证查到的一定是之前的元素。
代码关键行(Python)
def twoSum(nums, target):
seen = {} # 值 -> 下标
for i, x in enumerate(nums):
need = target - x # 还差的另一半
if need in seen: # 先查:之前出现过吗
return [seen[need], i]
seen[x] = i # 后存:没配上才存自己关键就两行:`if need in seen` 在前、`seen[x] = i` 在后。查的是「之前已存的元素」,当前数还没进表,天然避开「自己配自己」。
常见追问
先把所有数都存进哈希表、再统一查一遍,行不行?
不行。need = x(目标是某数两倍)而 x 只出现一次时,会查到自己、返回同一个下标两次。必须边遍历边查,「查在前、存在后」。
如果要返回的是两个数的值而不是下标呢?
逻辑不变,返回 [need, x] 即可。这种变体下排序 + 双指针也完全可行,因为不再需要原始下标。
把这道题彻底吃透
LC 1「两数之和」有逐步交互动画和完整图文题解,配着本页的结论看,一遍就通。