两数之和
在 LeetCode 上查看 ↗语音讲解
开车或通勤时可听,跟着思路走一遍
速度
题目描述
给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出和为目标值 target 的那两个整数,并返回它们的数组下标。你可以假设每种输入只会对应一个答案,且同一个元素不能使用两遍。
示例 1
输入:nums = [2,7,11,15], target = 9
输出:[0,1](因为 nums[0] + nums[1] == 9)
示例 2
输入:nums = [3,2,4], target = 6
输出:[1,2]
模拟答题者思考
1. 暴力:双重循环枚举所有 (i, j) 看和是否为 target,O(n²)。
2. 重复在哪?对每个 x 都在「重新遍历」找 target - x,其实只要知道它之前是否出现过。
3. 把「找另一半」变成查表:用哈希表存已扫过的值到下标,查找变 O(1)。
4. 边扫边存:先查 target - x 是否在表里,再把 x 存入,保证不会用到自己。
变量语义(先读这三句再编码)
| 变量 | 类型 | 语义(三句法) |
|---|---|---|
seen | map<int,int> | 定义:已扫描过的「值 → 下标」映射 维护:每轮结束后,seen 里存着 nums[0..i] 每个值最后出现的下标 更新:处理完 nums[i] 后 seen[nums[i]] = i |
target - x | int | 定义:当前元素 x 需要的「另一半」 维护:随 x 变化 更新:每轮用它去 seen 里查是否出现过 |
落码步骤
1. 初始化空哈希表 seen
2. 遍历数组,对每个 x = nums[i]:先查 target - x 是否在 seen 中
3. 若在,返回 [seen[target-x], i]
4. 否则把 seen[x] = i 记入历史
代码实现
class Solution:
def twoSum(self, nums: list[int], target: int) -> list[int]:
seen = {} # 值 -> 下标
for i, x in enumerate(nums):
if target - x in seen:
return [seen[target - x], i]
seen[x] = i
return []
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
unordered_map<int, int> seen; // 值 -> 下标
for (int i = 0; i < nums.size(); i++) {
auto it = seen.find(target - nums[i]);
if (it != seen.end()) return {it->second, i};
seen[nums[i]] = i;
}
return {};
}
};
// 时间 O(n),空间 O(n)
复杂度分析
时间复杂度
O(n)
空间复杂度
O(n)
常见坑
必须「先查后存」:如果先把 x 存进表再查,可能会把自己当成另一半(当 target = 2*x 时)。
返回的是下标不是值;题目保证恰有一个答案,无需继续遍历。
有重复值时哈希表会覆盖旧下标,但因为答案唯一,不影响正确性。
必测边界 Case
Case 1:包含负数
nums = [-3,4,3,90], target = 0 → [0,2]
Case 2:两个相同值
nums = [3,3], target = 6 → [0,1]