Problem overview
The method twoSum(_ nums: [Int], _ target: Int) receives an array of integers and a target. It must return the indices of two numbers that add up to target, as an array [i, j]. The input has at most one solution, and the same index cannot be used twice. When no pair works, the method returns [-1, -1].
In the example, nums = [2, 7, 11, 15] and target = 9. The values at indices 0 and 1 are 2 and 7, which add up to 9, so the answer is [0, 1].
Two equal values are fine as long as they sit at different indices: [3, 3] with target 6 gives [0, 1]. The answer is indices, not values.
Approach: check every pair of indices
The statement allows O(n^2) time and O(1) extra space, which fits the most direct idea: try every pair of positions and stop at the first pair whose values sum to the target.
To avoid wasted work, each pair should be tried once. The pair at positions 2 and 5 is the same pair as the one at 5 and 2. So the outer index i takes every position, and the inner index j starts right after it. That gives all pairs with i < j. It also makes using one index twice impossible, since j is never equal to i.
Faster solutions exist. A hash table of the values seen so far finds the partner of each number in constant time and brings the total to O(n). This lesson stays with brute force, which is the baseline to compare those against.
Step by step
- Loop over the first index.
for i in 0..<nums.countuses a half-open range, which stops beforenums.count, soitakes every valid index. - Loop over the second index. Inside it,
for j in (i + 1)..<nums.countstarts afteri. The parentheses make the range's start clear. - Test the pair. If
nums[i] + nums[j] == target, return[i, j]. Returning inside the loops ends the search at the first match. - Return the failure value. After both loops, write
return [-1, -1]. This line is reached only when every pair has been tried without a match.
The solution in Swift
class Solution {
func twoSum(_ nums: [Int], _ target: Int) -> [Int] {
for i in 0..<nums.count {
for j in (i + 1)..<nums.count {
if nums[i] + nums[j] == target {
return [i, j]
}
}
}
return [-1, -1]
}
}
The Swift details worth knowing:
- Empty ranges are legal, backwards ranges are not. When
iis the last index, the inner range isnums.count..<nums.count, which is empty, so the inner loop simply does nothing. That is why the outer loop can run to the end. A range whose start is above its end, such as4..<3, crashes the program at run time. Here the start never exceedsnums.count, so that cannot happen. - Empty input. For an empty array,
0..<0gives no iterations and the method returns[-1, -1]. - Array literals.
[i, j]is inferred as[Int]from the return type. - Overflow traps.
Intis 64 bits. Adding two values whose sum doesn't fit, such asInt.max + 1, stops the program with a run-time error instead of wrapping around. Ordinary test values never get near that.
Complexity
Time: O(n^2). The inner loop runs n - 1 times for i = 0, n - 2 times for i = 1, and so on, giving n(n - 1)/2 pair checks in the worst case, which is when no pair matches or the match is at the end. That is proportional to n^2.
Space: O(1). The method uses only the two loop variables. The returned two-element array has a fixed size.
Edge cases and common mistakes
- Using one element twice.
[3, 4]with target 6 must return[-1, -1]. An inner loop starting atiwould pair the 3 with itself and return[0, 0]. - Fewer than two elements.
[]and[5]with any target both return[-1, -1]. - No solution.
[1, 2, 3]with target 100 returns[-1, -1], and only after all three pairs have been checked. - Index order. The smaller index comes first. For
[7, 2]and target 9 the answer is[0, 1], never[1, 0]. - More than one match. The problem promises at most one pair. If there were several, the method would return the first pair found in loop order. For
[1, 5, 5, 1]with target 6 it returns[0, 1]. - Returning values. The method returns positions. For
[2, 7, 11, 15]the answer is[0, 1], not[2, 7]. - Negative numbers. The comparison is on sums, so negatives work without special handling:
[3, 9, -2, 5]with target 3 gives[2, 3].
Testing
nums = [2, 7, 11, 15],target = 9gives[0, 1].nums = [3, 9, -2, 5],target = 3gives[2, 3].nums = [3, 3],target = 6gives[0, 1].nums = [3, 4],target = 6gives[-1, -1].nums = [],target = 5gives[-1, -1].
