Problem overview
The function twoSum takes a slice of integers, nums, and a target. It returns the indices of two numbers at different positions that add up to target, or [-1, -1] when no pair exists. There is at most one solution, and the same index cannot be used twice.
For nums = [2, 7, 11, 15] and target = 9, the values 2 and 7 at indices 0 and 1 add up to 9. This solution returns the pair as [1, 0]: the index of the later element comes first, then the index of the earlier one.
The statement requires O(n) time and O(n) extra space.
Approach: remember what you have seen
The brute-force approach tries every pair of indices with two nested loops, which is O(n²). To remove one loop, fix the later element of the pair, nums[i]. Its partner must equal target - nums[i] and sit somewhere to its left. Asking "is this value among the elements already seen?" is a lookup, and a hash map answers it in O(1).
The map must also give back the partner's position, so store each value together with its index: the key is the number, the value is its index. For each i from left to right, first look for target - nums[i] in the map. If it is there, the answer is i and the stored index. If not, record nums[i] with index i and move on.
On the example: at i = 0 the needed value is 7, the map is empty, so 2 is stored with index 0. At i = 1 the needed value is 9 - 7 = 2, which is in the map with index 0, so the function returns [1, 0].
Step by step
- Create the map
indexOfValuewithmake, of typemap[int]int. It remembers where each value was last seen. - Loop over the indices with
for i := range nums. With one variable,rangeover a slice gives just the index. - Look up the complement in an
ifwith the comma-ok form, discarding the stored value and keeping the booleanexists. The lookup has to use comma-ok because the value 0 is a legitimate index. - On a hit, return a slice holding
iand the stored index. - Otherwise store the current element after the check. Storing it first would let an element pair with itself.
- Return
[]int{-1, -1}after the loop.
The solution in Go
func twoSum(nums []int, target int) []int {
indexOfValue := make(map[int]int)
for i := range nums {
if _, exists := indexOfValue[target - nums[i]] ; exists {
return []int{i, indexOfValue[target - nums[i]]}
}
indexOfValue[nums[i]] = i
}
return []int{-1, -1}
}
The Go details:
- Comma-ok in the
if. The statement before the semicolon runs first, andexistsis visible only inside theifand its branches. Reading a missing key would give 0, which could be mistaken for index 0, so the boolean is what decides. - Slice literals.
[]int{i, x}builds a new slice of two integers. - Why
make. A nil map cannot be written to;make(map[int]int)creates a usable one.
Complexity
Time: O(n). One pass, with a constant number of map operations per element, each O(1) on average.
Space: O(n). The map can hold one entry per element.
Edge cases and common mistakes
- Equal values.
[3, 3]with target 6 returns[1, 0]. The first 3 is stored before the second one is examined, so the two are different indices. - Using one element twice.
[3, 4]with target 6 returns[-1, -1]. Because the current element is stored after the lookup, the 3 is never matched with itself. - Empty input. The loop does not run and the function returns
[-1, -1]. - Index order. The later index comes first:
[3, 2, 4]with target 6 returns[2, 1]. - Negative numbers and zero.
[-3, 4, 3, 90]with target 0 returns[2, 0], and[0, 0]with target 0 returns[1, 0]. - Repeated values. If a value occurs several times, the map keeps the latest index for it. With at most one valid pair, that is enough.
Testing
[2, 7, 11, 15], target 9 gives[1, 0].[3, 3], target 6 gives[1, 0].[3, 4], target 6 gives[-1, -1].[1, 2, 3], target 100 gives[-1, -1].[], target 6 gives[-1, -1].