# 1. 两数之和
给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出和为目标值 target 的那两个整数,并返回它们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。
你可以按任意顺序返回答案。
示例 1:
输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。
示例 2:
输入:nums = [3,2,4], target = 6
输出:[1,2]
示例 3:
输入:nums = [3,3], target = 6
输出:[0,1]
提示:
2 <= nums.length <= 10^4-10^9 <= nums[i] <= 10^9-10^9 <= target <= 10^9- 只会存在一个有效答案
# 思路
暴力解法是两层 for 循环,对于每个元素,遍历它后面的所有元素,看有没有相加等于 target 的,时间复杂度 O(n²)。
能不能优化?
本题的核心需求是:遍历数组的时候,快速查找"之前有没有出现过某个元素"。
查询一个元素是否出现过——这就是哈希法的用武之地。
那么用什么哈希结构?
- 数组:大小受限,如果元素值跨度大(比如 -10^9 到 10^9),空间浪费严重
- set:只能存 key,但本题不仅要判断元素有没有出现过,还要知道它的下标,set 存不了下标
- map:key 存元素值,value 存下标,正好满足需求
所以本题用 map。
接下来明确两个问题:
- map 用来做什么——存放我们遍历过的元素,方便后续查询
- map 中 key 和 value 分别存什么——key 存元素值(用来判断是否出现过),value 存下标(题目要返回的)
还需要注意一点:key 不能重复,且不需要有序,所以选择 unordered_map,查询效率 O(1)。
# 模拟过程
以 nums = [2, 7, 11, 15],target = 9 为例。
i = 0,nums[0] = 2:
查找 target - 2 = 7,map 为空,没找到。把 {2, 0} 存入 map。
i = 1,nums[1] = 7:
查找 target - 7 = 2,在 map 中找到了!key=2 对应 value=0,说明 nums[0] + nums[1] = 2 + 7 = 9。返回 [0, 1]。
# 解题代码
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
unordered_map<int, int> map; // key: 元素值, value: 下标
for (int i = 0; i < nums.size(); i++) {
auto iter = map.find(target - nums[i]); // 查找之前是否出现过配对元素
if (iter != map.end()) {
return {iter->second, i};
}
map.insert(pair<int, int>(nums[i], i)); // 没找到,把当前元素存入map
}
return {};
}
};
2
3
4
5
6
7
8
9
10
11
12
13
14
时间复杂度:O(n)
空间复杂度:O(n)
# 其他语言
# Python3
class Solution:
def twoSum(self, nums: List[int], target: int) -> List[int]:
records = dict()
for index, value in enumerate(nums):
if target - value in records: # 查找之前是否出现过配对元素
return [records[target - value], index]
records[value] = index # 没找到,把当前元素存入字典
return []
2
3
4
5
6
7
8
# Java
class Solution {
public int[] twoSum(int[] nums, int target) {
Map<Integer, Integer> map = new HashMap<>();
for (int i = 0; i < nums.length; i++) {
int temp = target - nums[i]; // 查找之前是否出现过配对元素
if (map.containsKey(temp)) {
return new int[]{map.get(temp), i};
}
map.put(nums[i], i); // 没找到,把当前元素存入map
}
return new int[0];
}
}
2
3
4
5
6
7
8
9
10
11
12
13
# Go
func twoSum(nums []int, target int) []int {
m := make(map[int]int)
for i, num := range nums {
complement := target - num
if index, found := m[complement]; found { // 查找之前是否出现过配对元素
return []int{index, i}
}
m[num] = i // 没找到,把当前元素存入map
}
return nil
}
2
3
4
5
6
7
8
9
10
11
# JS
var twoSum = function(nums, target) {
let hash = {};
for (let i = 0; i < nums.length; i++) {
if (hash[target - nums[i]] !== undefined) { // 查找之前是否出现过配对元素
return [hash[target - nums[i]], i];
}
hash[nums[i]] = i; // 没找到,把当前元素存入对象
}
return [];
};
2
3
4
5
6
7
8
9
10
# 与代码随想录联系
我在代码随想录哈希表章节里,详细讲解过什么时候用数组做哈希,什么时候用 set,什么时候用 map。
如果想系统学习哈希表,可以看一下代码随想录哈希表章节 (opens new window),从理论基础到实战题目都有覆盖。
和本题相关的题目:
- 242. 有效的字母异位词 (opens new window)——用数组做哈希
- 349. 两个数组的交集 (opens new window)——用 set 做哈希
评论
验证登录状态...