Problem overview
The function twoSum receives an IntArray called nums and an Int called target. It returns the indices of two different positions whose values add up to target, or [-1, -1] if no such pair exists. There is at most one solution, and the same index can't be used twice, although equal values at different indices are fine.
For nums = [2, 7, 11, 15] and target = 9, the values at indices 0 and 1 are 2 and 7, so the answer is [0, 1]. The statement asks for O(n) time and O(n) space.
Approach: remember what you have seen
Trying every pair of indices with two nested loops is O(n²). To get to O(n), only one loop is allowed, so each element must find its partner without a search.
Fix the second element of the pair at index i. Its partner must equal target - nums[i] and must sit at an earlier index. If you've remembered every earlier value together with its index, finding the partner is one lookup. A hash map does that in constant time on average, with the value as the key and its index as the stored value, so the lookup returns the index that the answer needs.
The loop therefore does two things for each i: first look for target - nums[i] among the earlier elements, and return if it's there; otherwise record nums[i] and move on. Checking before storing also guarantees that an element is never paired with itself.
On nums = [3, 2, 4] and target = 6: at i = 0 the needed value is 3, the map is empty, so store 3 -> 0. At i = 1 the needed value is 4, still absent, so store 2 -> 1. At i = 2 the value 4 needs 2, which is in the map at index 1. The answer is [2, 1], with the current index first.
Step by step
- Create the map.
indexOfValueis aHashMap<Int, Int>declared withval. It maps each value seen so far to its index. - Loop over the indices.
for (i in nums.indices)treats eachnums[i]as the possible second element of the pair. - Look for the partner. If
indexOfValue.containsKey(target - nums[i]), the partner appeared earlier. Return anIntArraywithifirst and the stored index second. The lookupindexOfValue[target - nums[i]]gives anInt?, so the code adds!!, which is safe becausecontainsKeyjust confirmed the key is present. - Store the current element.
indexOfValue[nums[i]] = iadds the key, or overwrites it if the value was seen before. It happens after the check, so an element can't match itself. - Return
[-1, -1]when the loop ends without finding a pair.
The solution in Kotlin
class Solution {
fun twoSum(nums: IntArray, target: Int): IntArray {
val indexOfValue = HashMap<Int, Int>()
for (i in nums.indices) {
if (indexOfValue.containsKey(target - nums[i])) {
return intArrayOf(i, indexOfValue[target - nums[i]]!!)
}
indexOfValue[nums[i]] = i
}
return intArrayOf(-1, -1)
}
}
Kotlin details worth knowing:
nums.indicesis the range0..nums.size - 1. For an empty array it is empty, so the loop body never runs.- Map lookups are nullable.
indexOfValue[key]has typeInt?because a missing key givesnull.intArrayOfwants plainInts, so the nullable value must be converted.!!says "I know this isn't null" and throws aNullPointerExceptionif you're wrong. HerecontainsKeymakes it safe. The alternativeval j = indexOfValue[target - nums[i]]followed byif (j != null)does the lookup once instead of twice, and Kotlin then treatsjas a plainIntinside theif. map[key] = valueis the shorthand forput. It adds a new key or replaces the value of an existing one.intArrayOf(a, b)creates anIntArraywith the given elements. The function's return type isIntArray, notList<Int>.- Overflow.
target - nums[i]is anIntsubtraction, andIntwraps silently at 32 bits. For exampleInt.MAX_VALUE - (-5)gives -2147483644. With inputs near the limits, the wrapped number could match a stored value by accident; useLongif the inputs can be that large.
Complexity
Time: O(n). The loop visits each element once, and each iteration does a constant number of hash-map operations, each O(1) on average.
Space: O(n). In the worst case, when no pair matches, the map stores one entry per distinct value in the array.
This trades memory for speed. Sorting plus two pointers needs no map but costs O(n log n) time, and the brute force needs no extra space but takes O(n²).
Edge cases and common mistakes
- Index order. The result is the current index first, then the earlier one. For
[2, 7, 11, 15]and target 9, the code returns[1, 0], not[0, 1]. Both name the same two elements. - Using one element twice. For
[3, 4]and target 6, the answer is[-1, -1]. Storing3before checking would pair it with itself. - Equal values. For
[3, 3]and target 6 the answer is[1, 0]. The first 3 is stored at index 0, then the second 3 finds it. - Duplicates overwriting. If a value occurs twice, the map keeps the later index. That is fine here, since a pair is returned the moment it exists.
- No pair.
[1, 2, 3]with target 100 returns[-1, -1]. - Empty array. It returns
[-1, -1]. - Forgetting
!!. Passing the lookup straight tointArrayOfdoesn't compile, because the value is anInt?.
Testing
nums = [3, 2, 4],target = 6gives[2, 1].nums = [3, 3],target = 6gives[1, 0].nums = [3, 4],target = 6gives[-1, -1].nums = [1, 2, 3],target = 100gives[-1, -1].nums = [],target = 9gives[-1, -1].