3Sum is the classic next step after Two Sum. Instead of finding one pair, you find every unique trio that adds up to zero. The winning idea is to sort the array first, then fix one number and use two pointers to find the matching pair fast.
Problem. Given an integer array nums, return all unique triplets [a, b, c] such that
a + b + c = 0. The same element may not be reused, and the answer must not contain duplicate triplets.
Example: nums = [-1, 0, 1, 2, -1, -4] → answer [[-1, -1, 2], [-1, 0, 1]].
The idea
The brute force tries every trio with three nested loops — that is O(n³), far too slow. We can do much better.
First, sort the array. Now walk a fixed index i from left to right. For each nums[i], the other two numbers must sum to -nums[i]. Finding a pair with a known sum in a sorted array is the two-pointer trick: put lo just after i and hi at the end.
- If
nums[lo] + nums[hi]is too small, moveloright to make the sum bigger. - If it is too big, move
hileft to make the sum smaller. - If it is exactly the target, record the triplet and move both pointers inward.
Sorting is what makes this work: it turns "search the whole array" into "nudge a pointer in the right direction."
The other job is avoiding duplicates. Because the array is sorted, equal numbers sit next to each other. So we skip a fixed i if it equals the previous one, and after recording a hit we skip equal lo/hi values too. That keeps every triplet unique without a separate dedup pass.
Walk through it
Step through the animation. The array is shown already sorted: [-4, -1, -1, 0, 1, 2]. The pointer i fixes one number; lo and hi close in from both sides. Watch the fixed value -1 (at i = 1): the sweep finds two triplets, [-1, -1, 2] and [-1, 0, 1]. Then the second -1 is skipped as a duplicate, and 0 finds nothing because its smallest pair already overshoots zero.
Pseudocode
sort nums
result = empty list
for i from 0 to n - 3:
if i > 0 and nums[i] == nums[i - 1]:
continue # skip a duplicate fixed value
lo = i + 1
hi = n - 1
while lo < hi:
s = nums[i] + nums[lo] + nums[hi]
if s == 0:
record [nums[i], nums[lo], nums[hi]]
move lo right, move hi left
skip lo/hi over equal values # avoid duplicate triplets
else if s < 0:
move lo right # sum too small
else:
move hi left # sum too big
return resultThe outer loop fixes one number; the inner two-pointer loop scans the rest in linear time.
The Python solution
def three_sum(nums):
nums.sort()
res = []
for i in range(len(nums) - 2):
if i > 0 and nums[i] == nums[i - 1]:
continue
lo, hi = i + 1, len(nums) - 1
while lo < hi:
s = nums[i] + nums[lo] + nums[hi]
if s == 0:
res.append([nums[i], nums[lo], nums[hi]])
lo += 1
hi -= 1
elif s < 0:
lo += 1
else:
hi -= 1
return resnums.sort()is what unlocks the two-pointer move — without it, nudginglo/hiwould be meaningless.- The outer loop fixes
nums[i]; theif i > 0 and nums[i] == nums[i - 1]guard skips a repeated fixed value so we never emit the same triplet twice. s = nums[i] + nums[lo] + nums[hi]is the running sum of the current trio.- When
s == 0we record the triplet and move both pointers inward. Whens < 0the sum is too small sologoes right; whens > 0it is too big sohigoes left. - A production version also skips equal
lo/hivalues right after a hit — the animation leaves that detail out to keep the line mapping simple, but the prose dedup guard oniis the key idea.
Complexity
| Case | Time | Notes |
|---|---|---|
| Brute force (every trio) | O(n³) (moderate) | three nested loops |
| Sort + two pointers | O(n²) (slow) | n fixed values × O(n) sweep |
O(1) (fast)Sorting costs O(n log n), which is dwarfed by the O(n²) sweep. The extra space is O(1) beyond the output list (the sort is in place). The big win is O(n³) → O(n²) — the same "sort, then two pointers" pattern that powers 3Sum Closest, 4Sum, and many container/interval problems.
When this pattern shows up
Whenever a problem says "find numbers that sum to a target" and the input can be sorted, reach for sort + two pointers. Two Sum (sorted), 3Sum, 3Sum Closest, and 4Sum are all the same core move: fix what you can, then walk two pointers inward to close the gap.
Duplicates are where most 3Sum attempts fail. You must skip a repeated fixed value (nums[i] == nums[i - 1])
and skip equal lo/hi values after a hit. Forgetting either one produces duplicate triplets in the answer.
Practice
After sorting to [-4, -1, -1, 0, 1, 2] and fixing i = 1 (value -1), the pointers find [-1, -1, 2] then [-1, 0, 1]. What happens when i reaches the second -1 (index 2)?
1. Why do we sort the array before using two pointers?
2. When nums[i] + nums[lo] + nums[hi] is greater than 0, which pointer moves?
3. What is the time complexity of the sort + two-pointer solution?
4. How are duplicate triplets avoided?