题目:梦开始的地方

  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 <= 104
  • -109 <= nums[i] <= 109
  • -109 <= target <= 109

只会存在一个有效答案

进阶:你可以想出一个时间复杂度小于 O(n2) 的算法吗?

解题思路

这个第一反应就是用 HashMap 来存储数组的值和下标,然后遍历数组:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
func twoSum(nums []int, target int) []int {
numMap := make(map[int][]int)
for i := 0; i < len(nums); i++ {
if _, ok := numMap[nums[i]]; ok {
numMap[nums[i]] = append(numMap[nums[i]], i)
} else {
numMap[nums[i]] = make([]int, 1)
numMap[nums[i]][0] = i
}
}

for i := 0; i < len(nums); i++ {
x := target - nums[i]

if idx, ok := numMap[x]; ok {
if nums[i] == x {
if len(idx) < 2 {
continue
}

return []int{idx[0], idx[1]}
}

return []int{i, idx[0]}
}
}

return []int{}
}

值得注意的是,如果 nums[i]x 相等的话,必须要判断一下 idx 的长度是否大于 2,因为如果只有一个元素的话,就不能返回两个下标了。还有在开始写的时候, numMap 的键一开始写错了,导致找了一段时间才发现问题。最后的时间复杂度是 O(n),空间复杂度也是 O(n),因为需要存储 HashMap 的值和下标。

进一步优化,可以做到一个循环里完成:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
func twoSum(nums []int, target int) []int {
numMap := make(map[int]int)

for i, val := range nums {
x := target - val
if idx, ok := numMap[x]; !ok {
numMap[val] = i
} else {
return []int{i, idx}
}
}

return []int{}
}

题目不难,这里特意用了 range 语法来遍历数组,i 是下标,val 是值。这样就可以直接使用 val 来作为 map 的键了,可以一定程度上避免之前的错误。最后的时间复杂度是 O(n),空间复杂度也是 O(n),因为需要存储 HashMap 的值和下标。