Two Sum
LeetCode ↗ Java source ↗ Edit this article ↗
Jump to code:
Problem
You are given an array of integers nums and an integer target. Return the indices i and j (with i < j) of two different elements whose values add up to target. If no such pair exists, return {-1, -1}. Some versions of the problem only ask whether such a pair exists; the same approaches answer that too.
Constraints: 2 ≤ n ≤ 10^5, values and target fit in a 32-bit signed integer. Exactly one valid answer exists in the LeetCode version.
Example 1
nums = [2, 7, 11, 15], target = 9
answer = [0, 1] // 2 + 7 = 9
Example 2
nums = [3, 3], target = 6
answer = [0, 1] // the two 3s are different elements, so this is allowed
Example 3
nums = [1, 2, 3], target = 7
answer = [-1, -1] // no two elements sum to 7
Approach 1: Brute force
Intuition. The definition of the problem is “some pair sums to target”, so the most direct method is to look at every pair. Fix the first element with an outer loop, and scan everything after it with an inner loop. Starting the inner loop at i + 1 guarantees we never pair an element with itself and never check the same pair twice.
Algorithm.
- For every index
ifrom0ton - 1: - For every index
jfromi + 1ton - 1: - If
nums[i] + nums[j] == target, return{i, j}. - If the loops finish, return
{-1, -1}.
static int[] bruteForce(int[] nums, int target) {
int n = nums.length;
for (int i = 0; i < n; i++) {
for (int j = i + 1; j < n; j++) {
if ((long) nums[i] + nums[j] == target) return new int[]{i, j};
}
}
return new int[]{-1, -1};
}
Complexity. Time O(n^2): there are n(n-1)/2 pairs and each is checked in constant time. Space O(1): only loop counters.
The (long) cast matters. Two values near 2 × 10^9 add up to about 4 × 10^9, which does not fit in an int; the wrapped-around result could accidentally equal the target and report a pair that does not really exist.
For n = 10^5 that is about 5 × 10^9 comparisons, which will not pass in time. We need to avoid checking every pair.
Approach 2: Better, sorting plus two pointers
Intuition. If the array were sorted, we could place one pointer at the smallest value and one at the largest. If their sum is too small, the only way to increase it is to move the left pointer right. If the sum is too big, move the right pointer left. Each step discards one element for good, so we finish in one linear sweep.
The catch is that the question asks for the original indices, and sorting destroys them. So we sort pairs of {value, originalIndex} instead of bare values.
Algorithm.
- Build an array of
{value, originalIndex}pairs and sort it by value. - Set
lo = 0andhi = n - 1. - While
lo < hi: computesum = value[lo] + value[hi].- If
sum == target, return the two original indices in increasing order. - If
sum < target, dolo++. Otherwise dohi--.
- If
- Return
{-1, -1}if the pointers meet.
static int[] better(int[] nums, int target) {
int n = nums.length;
int[][] a = new int[n][2]; // {value, originalIndex}
for (int i = 0; i < n; i++) a[i] = new int[]{nums[i], i};
Arrays.sort(a, (x, y) -> Integer.compare(x[0], y[0]));
int lo = 0, hi = n - 1;
while (lo < hi) {
long sum = (long) a[lo][0] + a[hi][0];
if (sum == target) {
int i = Math.min(a[lo][1], a[hi][1]), j = Math.max(a[lo][1], a[hi][1]);
return new int[]{i, j};
}
if (sum < target) lo++; else hi--;
}
return new int[]{-1, -1};
}
Complexity. Time O(n log n) for the sort; the two-pointer sweep is O(n). Space O(n) for the array of pairs.
Note the long cast in the sum. Two values near 2 × 10^9 overflow int when added, and an overflowed sum could accidentally equal the target.
This approach is the one to remember when the problem is a yes/no question or when the input is already sorted, because then no index bookkeeping is needed and the extra space disappears.
Approach 3: Optimal, one pass with a hash map
Intuition. While scanning left to right, at position j we want to know: “have I already seen the value target - nums[j], and if so, where?” A hash map from value to index answers that in constant time. Insert each element after checking, so an element can never be paired with itself.
Algorithm.
- Create an empty map
seenfrom value to index. - For each index
j: letneed = target - nums[j].- If
needis inseen, return{seen.get(need), j}. - Otherwise store
seen.put(nums[j], j).
- If
- Return
{-1, -1}if the loop finishes.
One subtlety: target - nums[j] can itself overflow int (for example -294967296 - 2000000000). Computing need as a long and skipping the lookup when it is outside the int range keeps the method exact; any value outside that range cannot be in the array anyway.
static int[] optimal(int[] nums, int target) {
Map<Integer, Integer> seen = new HashMap<>();
for (int j = 0; j < nums.length; j++) {
long need = (long) target - nums[j]; // long: target - nums[j] can overflow int
if (need >= Integer.MIN_VALUE && need <= Integer.MAX_VALUE) {
Integer i = seen.get((int) need);
if (i != null) return new int[]{i, j};
}
seen.put(nums[j], j);
}
return new int[]{-1, -1};
}
Dry run on nums = [2, 7, 11, 15], target = 9:
| j | nums[j] | need | need in seen? | action |
|---|---|---|---|---|
| 0 | 2 | 7 | no | seen = {2→0} |
| 1 | 7 | 2 | yes, index 0 | return {0, 1} |
Complexity. Time O(n): one pass, each map operation is O(1) on average. Space O(n) for the map in the worst case.
Because duplicates are stored with their latest index only after the check, [3, 3] with target 6 correctly returns {0, 1}: at j = 1, need = 3 is already present from j = 0.
Edge cases and pitfalls
- Pairing an element with itself.
[5]with target10must return no answer. Checking the map before inserting, or starting the inner loop ati + 1, prevents this. - Duplicates.
[3, 3]is a valid pair because the indices differ. Do not de-duplicate the input. - Overflow. Both
nums[i] + nums[j]andtarget - nums[j]can overflowint. All three methods do that arithmetic inlong. The test in the Java file uses[2000000000, 2000000000]with target-294967296, which is exactly what naiveintaddition wraps to, and expects no pair. - Negative numbers. Nothing special is needed; the map and the sorted sweep handle them.
- Return order. Return the smaller index first. The sorted approach can find them in either order, so normalise with
minandmax.
Related problems
- 3 Sum extends the two-pointer idea to three elements.
- 4 Sum adds one more level of fixing elements.
- Two sum in BST applies the same idea to an in-order traversal.
- Longest Subarray with sum K uses the same “have I seen the complement” trick on prefix sums.