Two Sum
Step-by-step intuition, pointer tracing, complexity tradeoffs, and multi-language implementations.
Two Sum
Find two distinct indices in an integer array whose values sum to a given target.
Problem Statement
Given an array of integers nums and an integer target, return the indices of the two numbers such that they add up to target.
You may assume that each input has exactly one valid solution, and you may not use the same element twice. You can return the answer in any order.
Constraints
2 <= nums.length <= 10^4-10^9 <= nums[i] <= 10^9-10^9 <= target <= 10^9- Only one valid answer exists.
Core Intuition
The naive approach checks every possible pair using two nested loops. For each index i, an inner loop scans all subsequent indices j to test if nums[i] + nums[j] == target. Because testing every pair of an array of size $n$ requires approximately $n^2 / 2$ checks, this brute-force method runs in $O(n^2)$ time.
Instead of scanning the array repeatedly to find the matching number, rewrite the equation:
$$\text{complement} = \text{target} - \text{nums}[i]$$
For any number $\text{nums}[i]$, there is only one exact value that pairs with it to reach target. By storing each number we visit in a hash map as a key, paired with its index as the value, we can verify whether the needed complement was already seen in $O(1)$ average time.
A single pass through the array suffices. At each element, compute its complement. If the complement is already in the hash map, return the complement's index along with the current index. If not, record the current element and its index in the map, then move forward.
Step-by-Step Walkthrough
Consider nums = [3, 2, 4, 8] with target = 6.
Initial State
- Array:
[3, 2, 4, 8] - Hash Map:
{}
Step 1: Index 0
- Current element:
nums[0] = 3 - Needed complement:
6 - 3 = 3 - Check map: Is
3in{}? No. - Store
nums[0]in map:{ 3: 0 }
Indices: 0 1 2 3
Nums: [ 3, 2, 4, 8 ]
↑
i = 0 (val = 3, complement = 3)
Map: { 3: 0 }
Step 2: Index 1
- Current element:
nums[1] = 2 - Needed complement:
6 - 2 = 4 - Check map: Is
4in{ 3: 0 }? No. - Store
nums[1]in map:{ 3: 0, 2: 1 }
Indices: 0 1 2 3
Nums: [ 3, 2, 4, 8 ]
↑
i = 1 (val = 2, complement = 4)
Map: { 3: 0, 2: 1 }
Step 3: Index 2
- Current element:
nums[2] = 4 - Needed complement:
6 - 4 = 2 - Check map: Is
2in{ 3: 0, 2: 1 }? Yes! - Complement
2was stored at index1. - Return
[1, 2].
Indices: 0 1 2 3
Nums: [ 3, 2, 4, 8 ]
↑
i = 2 (val = 4, complement = 2)
Map: { 3: 0, 2: 1 } -> Found key 2 at index 1!
Result: [1, 2]
Algorithm Steps
- Initialize an empty hash map
seento store array values as keys and their corresponding indices as values. - Iterate through
numsusing an indexifrom0ton - 1. - For each element
nums[i], calculatecomplement = target - nums[i]. - If
complementexists inseen, return[seen[complement], i]. - If
complementdoes not exist inseen, insertseen[nums[i]] = i. - Continue to the next element. The problem guarantees exactly one solution exists.
Complexity Analysis
- Time Complexity: $O(n)$ — The array is traversed once. Each lookup and insertion into the hash map takes $O(1)$ time on average.
- Space Complexity: $O(n)$ — In the worst case, the matching pair is at the very end of the array, requiring the hash map to store up to $n - 1$ entries.
class Solution:
def twoSum(self, nums: list[int], target: int) -> list[int]:
# Maps value -> index
seen = {}
for i, num in enumerate(nums):
complement = target - num
if complement in seen:
return [seen[complement], i]
seen[num] = i
return []Python 3
class Solution:
def twoSum(self, nums: list[int], target: int) -> list[int]:
# Maps value -> index
seen = {}
for i, num in enumerate(nums):
complement = target - num
if complement in seen:
return [seen[complement], i]
seen[num] = i
return []
JavaScript
/**
* @param {number[]} nums
* @param {number} target
* @return {number[]}
*/
function twoSum(nums, target) {
// Map value -> index
const seen = new Map();
for (let i = 0; i < nums.length; i++) {
const complement = target - nums[i];
if (seen.has(complement)) {
return [seen.get(complement), i];
}
seen.set(nums[i], i);
}
return [];
}
TypeScript
function twoSum(nums: number[], target: number): number[] {
// Map value -> index
const seen = new Map<number, number>();
for (let i = 0; i < nums.length; i++) {
const complement = target - nums[i];
const complementIndex = seen.get(complement);
if (complementIndex !== undefined) {
return [complementIndex, i];
}
seen.set(nums[i], i);
}
return [];
}
Java
import java.util.HashMap;
import java.util.Map;
class Solution {
public int[] twoSum(int[] nums, int target) {
// Map value -> index
Map<Integer, Integer> seen = new HashMap<>();
for (int i = 0; i < nums.length; i++) {
int complement = target - nums[i];
if (seen.containsKey(complement)) {
return new int[] { seen.get(complement), i };
}
seen.put(nums[i], i);
}
return new int[0];
}
}
C++
#include <vector>
#include <unordered_map>
class Solution {
public:
std::vector<int> twoSum(std::vector<int>& nums, int target) {
// Map value -> index
std::unordered_map<int, int> seen;
for (int i = 0; i < static_cast<int>(nums.size()); ++i) {
int complement = target - nums[i];
auto it = seen.find(complement);
if (it != seen.end()) {
return { it->second, i };
}
seen[nums[i]] = i;
}
return {};
}
};
Common Pitfalls & Edge Cases
- Using the Same Element Twice: If
nums = [3, 2, 4]andtarget = 6, looking for complement6 - 3 = 3must not matchnums[0]with itself. The one-pass hash map avoids this because the current element is added to the map after checking for its complement. - Duplicate Values in the Input: If
nums = [3, 3]andtarget = 6, pre-populating the entire hash map in advance can overwrite the first index with the second index. The one-pass approach processes the first3, stores index0, and on encountering the second3, successfully matches with index0. - Negative Numbers and Zeroes: Complements work identical mathematically with negative integers (e.g.,
target = -8,nums[i] = -2,complement = -8 - (-2) = -6). Standard hash map lookups handle negative keys without special modifications.
