Two Sum Solution
Given an array of integers and a target, return indices of the two numbers that add up to the target. Exactly one solution exists. You may not use the same element twice.
Problem Statement
Given an array of integers nums and an integer target, return the indices of the two numbers that add up to target. Each input has exactly one solution, and you may not use the same element twice.
Brute Force โ O(nยฒ)
The most straightforward approach: for every element, scan the rest of the array looking for its complement. Two nested loops โ the outer picks the first number, the inner searches for target - nums[i].
By checking every pair (i, j) where j > i, we guarantee we find the pair that sums to target. It's correct but slow โ O(nยฒ) comparisons.
| Metric | Value |
|---|---|
| Time | O(nยฒ) โ every pair checked |
| Space | O(1) โ no extra storage |
HashMap โ O(n)
The key insight: as we scan the array, we need to answer "have I already seen the number that would complete this pair?" โ that's a lookup problem. A HashMap maps each value to its index, giving O(1) lookups.
- Step 1: Create an empty HashMap<Integer, Integer> โ maps value โ index.
- Step 2: For each element nums[i], compute
complement = target - nums[i]. - Step 3: Check if complement exists in the map โ if yes, return [map.get(complement), i].
- Step 4: If not, store nums[i] โ i in the map and continue.
- Any time a problem says "find two elements that satisfy condition X" โ especially sums, differences, or complements โ think HashMap complement lookup.
- If you're scanning an array and asking "have I seen the other half before?", a HashMap turns that O(n) scan into O(1).
- This pattern appears in Two Sum, Three Sum (as a sub-routine), pair-with-given-difference, and subarray-sum problems.
- We store each element after checking for its complement.
- This guarantees we never match an element with itself (the "don't use same element twice" constraint) โ because an element is only added to the map after it has been processed as a potential match.
- When we find a complement, it must be a different, earlier element.
Visual Walkthrough
Trace through nums = [2, 7, 11, 15], target = 9:
- We found the answer on the second element. Steps 3 and 4 are greyed out โ they never execute.
- In the best case, we find the pair early and exit.
- In the worst case (answer at the end), we'd scan the entire array once building up the map โ still O(n).
Code โ Java & Python
Complexity Analysis
| Approach | Time | Space | Trade-off |
|---|---|---|---|
| Brute Force | O(nยฒ) | O(1) | No extra memory, but too slow for n = 10โด |
| Sort + Two Pointers | O(n log n) | O(n) | Need to track original indices (sort destroys them) |
| HashMap โ optimal | O(n) | O(n) | One pass, O(1) per lookup. Space for the map. |
- Sorting would give O(n log n) time with two pointers โ but the problem asks for indices, not values.
- Sorting destroys index information, so you'd need to store (value, originalIndex) pairs, adding complexity.
- The HashMap approach is simpler, faster (O(n)), and directly returns indices.
Edge Cases & Pitfalls
| Case | Input | Why It Matters |
|---|---|---|
| Negative numbers | [-1, -2, -3, -4, -5], target=-8 | HashMap handles negatives โ complement lookup works the same way. |
| Zeros | [0, 4, 3, 0], target=0 | Two zeros sum to 0. One-pass ensures we find the second zero after storing the first โ [0, 3]. |
| Duplicate values | [3, 3], target=6 | We check for complement before storing โ so when we process the second 3, the first 3 is already in the map. |
| Minimum array | [1, 2], target=3 | Smallest valid input. Works correctly โ i=0 stores 1, i=1 finds complement 1 in map. |
| Large values | [10โน, -10โน], target=0 | No overflow risk โ we only do subtraction and comparison. Java int range handles ยฑ2ร10โน. |