LEETCODE 001Easy
两数之和
用一次遍历和一张哈希表,把“寻找另一个数”从线性搜索降到常数时间。
问题拆解
给定一个整数数组 nums 和目标值 target,需要找到两个不同位置的数,让它们之和等于目标值。
暴力做法会枚举所有数对,时间复杂度是 O(n²)。真正值得抓住的问题是:当遍历到 x 时,我们只需要知道 target - x 是否已经出现过。
把“寻找另一个数”换成“查询另一个数”,哈希表正好适合做这件事。
一次遍历
public int[] twoSum(int[] nums, int target) {
Map<Integer, Integer> seen = new HashMap<>();
for (int i = 0; i < nums.length; i++) {
int need = target - nums[i];
if (seen.containsKey(need)) {
return new int[]{seen.get(need), i};
}
seen.put(nums[i], i);
}
return new int[0]; // 题目保证有解,走不到这里
}
def twoSum(nums: list[int], target: int) -> list[int]:
seen = {}
for index, value in enumerate(nums):
need = target - value
if need in seen:
return [seen[need], index]
seen[value] = index
return [] # 题目保证有解,走不到这里
func twoSum(nums []int, target int) []int {
seen := make(map[int]int)
for i, v := range nums {
if j, ok := seen[target-v]; ok {
return []int{j, i}
}
seen[v] = i
}
return nil // 题目保证有解,走不到这里
}
pub fn two_sum(nums: Vec<i32>, target: i32) -> Vec<i32> {
let mut seen = std::collections::HashMap::new();
for (i, &v) in nums.iter().enumerate() {
if let Some(&j) = seen.get(&(target - v)) {
return vec![j as i32, i as i32];
}
seen.insert(v, i);
}
vec![] // 题目保证有解,走不到这里
}
为什么先查询再写入?因为题目要求不能重复使用同一个元素。如果先放入当前数字,当 target == value * 2 时,就可能错误地匹配到自己。
复杂度
| 指标 | 复杂度 | 原因 |
|---|---|---|
| 时间 | O(n) |
数组只遍历一次 |
| 空间 | O(n) |
最坏情况下保存所有元素 |
可以迁移的模式
- 题目要求快速判断“某个值是否出现过”;
- 需要保存“值 → 位置”或“值 → 次数”的映射;
- 一边遍历,一边利用之前的信息完成当前决策。
下次看到“配对”“补数”“是否存在”这些词,可以先想想哈希表。