3Sum
3Sum is LeetCode 15 (Medium). You get an integer array nums and must return every unique triplet of values that adds up to exactly zero.
- The three values must come from three different positions
i,jandk; one element cannot be used twice. - Each triplet is reported once. Two triplets holding the same three values, in any order, count as the same answer.
- The triplets, and the values inside each one, may be returned in any order.
- If no three values sum to zero, return an empty list.
The array holds 3 to 3,000 numbers, each between −10⁵ and 10⁵. At 3,000 elements, checking every triple is about 4.5 billion sums, far too slow; an O(n²) scan is about 9 million steps.
- 3 <= nums.length <= 3000
- -10⁵ <= nums[i] <= 10⁵
Intuition
Trying every triple is O(n³). 3Sum drops to O(n²) by turning it into many small Two Sum problems on a sorted array. Fix the first number, and the other two must add up to its negative.
On a sorted array that pair is found with two pointers from the ends: a sum that is too small can only grow by moving the left pointer, and one that is too big can only shrink by moving the right. Each fixed number costs one O(n) sweep.
Sorting also handles uniqueness. Equal values sit next to each other, so skipping repeats means each triplet is produced exactly once.
"Find k numbers with a target sum" on an unsorted array: sort it, fix k − 2 numbers with loops, and finish with a two-pointer scan. 3Sum Closest (16) keeps the same scan and tracks the nearest sum; 4Sum (18) adds one more fixed loop for O(n³).
Approach
Before reading on, sort [-1, 0, 1, 2, -1, -4] and run the two pointers by hand for i = 1. Then say which lines stop [-1, 0, 1] from being reported twice.
Two ways to solve it
Sort, fix each nums[i], then move left and right inward to find pairs that sum to -nums[i].
- Speed: one linear pass per
i, so O(n²) in total. - Duplicates: skipped in place, no set needed.
- Space: only the pointers, besides the output and sort.
This is the standard interview answer.
Try every i < j < k, and when the three values sum to 0, sort them and add them to a set.
- Duplicates: the set keeps one copy of each sorted triplet.
- Scale: with
nup to 3000 that is about 4.5 billion checks, too slow. - Use: a simple checker for small tests.
Correct, but it times out on LeetCode.
Sorting lets two pointers replace the third loop, cutting O(n³) to O(n²) and removing the need for a set. The steps, code and live demo below follow two pointers, and the brute-force code is further down.
Sort the array
O(n log n). After sorting, pointer moves change the sum in a known direction, and duplicates are adjacent. In the 3Sum Python code nums.sort() sorts the list in place, so no copy is made.
Fix the first number
Loop i from 0 to n − 3:
nums[i] > 0– break. It is the smallest of the three, so no sum can be 0.nums[i] == nums[i − 1]– skip. Every triplet starting with this value was already found.
Close in with two pointers
Set L = i + 1, R = n − 1, and while L < R compute total:
total < 0– moveLright (need a bigger value).total > 0– moveRleft (need a smaller value).total == 0– record the triplet, move both pointers in, then moveLpast any value equal to the one just used.
Why no triplet is missed
When the sum is too small, nums[L] paired with the largest remaining value still falls short, so nums[L] pairs with nothing in the window and can be discarded. The same argument discards nums[R] when the sum is too big. Every move removes only values that cannot be part of an answer.
3Sum solution in Python | C++ | Java
Brute force with a set
Three nested loops try every triple of indices. Each zero-sum triple is sorted before it goes into the set, so [-1, 0, 1] and [0, -1, 1] count once.
Common pitfalls
Skipping duplicates by looking ahead at i
if nums[i] == nums[i + 1]:
continueif i > 0 and nums[i] == nums[i - 1]:
continueLooking ahead skips the first copy, so [-1, -1, 2] is never found: by the time i reaches the second -1, the other -1 is behind it. Compare with the previous value instead.
Stopping after the first match for an i
else:
result.append([nums[i], nums[left], nums[right]])
breakelse:
result.append([nums[i], nums[left], nums[right]])
left += 1
right -= 1One i can have several partners: with i = -4 in [-4, 0, 1, 3, 4] both [−4, 0, 4] and [−4, 1, 3] are answers.
Breaking on zero instead of only on positives
if nums[i] >= 0:
breakif nums[i] > 0:
breakWith nums[i] == 0 the triplet [0, 0, 0] is still possible. Breaking on >= 0 returns [] for [0, 0, 0] instead of [[0, 0, 0]].
Edge cases
[0, 0, 0, 0]One triplet [0, 0, 0]. The skip at i and at L stops it from being added three times.
[-2, 0, 0, 2, 2]Only [-2, 0, 2]. The second 0 is skipped at L; the extra 2 is passed over because the sum no longer matches.
Complexity
3Sum FAQ
Can 3Sum be solved faster than O(n²)?
Not in general. O(n²) is the best known for comparison-based 3Sum, and the "3SUM conjecture" in complexity theory assumes no algorithm is meaningfully faster. For interviews, O(n²) is the expected optimum.
What is the difference between Two Sum and 3Sum?
Two Sum returns indices of one pair and uses a hash map in O(n). 3Sum returns values of all unique triplets, so it sorts and runs a two-pointer Two Sum for each fixed first number, O(n²).