两数之和(LeetCode #1)
两数之和要求返回数组 nums 中相加等于 target 的两个索引。一次遍历的哈希表解法在每个元素处查询“补数是否出现过”,用 O(n) 额外空间,把暴力的 O(n²) 比较降到期望 O(n) 时间。
像门口维护来客名单:新人到达,先查配对的人是否已在场;没有再登记新人。先查后写是算法的关键。
两数之和如何工作
从左到右遍历,seen 保存值到索引的映射。在 i 处计算 complement = target - nums[i]。如果补数已在表中,返回 [seen[complement], i],较早索引在前;未命中才写 seen[nums[i]] = i。
若先写再查,遇到 nums[i] == target / 2 就可能与自身配对。查旧表保证其中只含之前的索引,两个位置因此不同。
暴力法使用双循环检查 j > i 的每一对,最多 n(n − 1)/2 对,额外空间 O(1)。输入面板的解法选项可在默认哈希表和暴力之间切换,相同输入便于比较。
逐步演示
默认 nums = [2, 7, 11, 15]、target = 18,采用哈希表:
i = 0、值 2:complement = 18 − 2 = 16。空表中没有 16,写seen[2] = 0。i = 1、值 7:complement = 18 − 7 = 11。未出现,写seen[7] = 1,表为{2: 0, 7: 1}。i = 2、值 11:complement = 18 − 11 = 7。表中索引为 1,返回[1, 2],不再访问 15。
切换暴力法,依次检查 2 + 7 = 9、2 + 11 = 13、2 + 15 = 17、7 + 11 = 18,四次后得到 [1, 2]。两者结果相同;规模增大时,暴力最坏检查数按平方增长,哈希表最多访问 n 个元素。
画布右侧是数组,左侧是“值 | 索引”表。每次先高亮元素、计算补数、查询字典,再把配对两处同时标绿,或写入一行(新增,值重复时覆盖原行)。暴力模式没有字典面板,显示其额外空间的区别。
时间与空间复杂度
| 情况 | 暴力法 | 哈希表法 |
|---|---|---|
| 最好 | O(1),首对命中 | O(1),索引 0、1 配对 |
| 扫描到末尾的时间 | O(n²) | 期望 O(n) |
| 额外空间 | O(1) | O(n) |
哈希表的界依赖期望 O(1) 查询,恶劣冲突下不是无条件最坏 O(n)。默认 n = 4 时动画步数差距有限;扩大输入,才能看清平方枚举与线性遍历的增长差异。
边界情况
播放器允许 3–8 个、范围 1–99 的值,其余情况可在纸上或代码中检验。
- 无解:原题保证恰好一解,但展示代码走到末尾时 Python 返回
None。动画明确展示无解状态,调用者应处理。 - 两个相等值组成答案:
nums = [3, 3, 4]、target = 6时,i = 1 查到索引 0 的 3,返回[0, 1]。先查后写保证不是自身配对。 - 重复值不在答案中:后来的同值覆盖索引;旧索引在被覆盖前已经查过自己的补数,寻找任一合法答案仍安全。
- 负数与零:整数键无需特别处理,只是显示输入范围不包含它们。
常见错误
- 先插入再查询。 可能把半目标值与自己配对。
- 返回值而非索引。 题目要位置,所以字典的值要保存索引。
- 排序后丢失原索引。 双指针可解,但要同时携带原位置;不能直接返回排序后的索引。
- 只用集合。 集合能回答是否出现,不能给出题目要求的位置。