Algorithm

3Sum

Two Pointer Pattern

3Sum

Given an integer array nums, return all unique triplets [a, b, c] drawn from three different positions such that a + b + c == 0. The result must not contain duplicate triplets (two triplets with the same three values, in any order, count as the same). The order of the triplets, and the order of values within each triplet, does not matter. Return the values themselves, not their indices. If no triplet sums to zero, return an empty list.

CONSTRAINTS
  • 3 <= nums.length <= 3000
  • -10⁵ <= nums[i] <= 10⁵
  • The result must not contain duplicate triplets
EXAMPLE 1
Input: nums = [-1,0,1,2,-1,-4]
Output: [[-1,-1,2],[-1,0,1]]
These are the only two distinct value-triplets that sum to zero; the second -1 in the input does not create a new one.
EXAMPLE 2
Input: nums = [0,0,0]
Output: [[0,0,0]]
0 + 0 + 0 = 0. There is one triplet of values even though it uses three positions.
EXAMPLE 3
Input: nums = [1,2,-2,-1]
Output: []
No three of these values add up to zero, so the answer is empty.
Can the result contain duplicate triplets?
No. Two triplets with the same three values (in any order) count as the same and must appear only once.
Do I return indices or the actual values?
The values. Positions do not appear in the output.
Does the order of triplets, or of numbers inside a triplet, matter?
No — any ordering is accepted.
What if no triplet sums to zero?
Return an empty list.

We need every group of three numbers that adds up to zero, with no repeated triplet. The honest brute force is three nested loops over all triples — about N³/6 of them — checking each sum. At N = 3000 that is billions of checks. We can do much better, and the key is to notice we already know how to find two numbers with a target sum quickly.

Turn "three numbers" into "one plus a pair"

Pick one number and call it the anchor. Once the anchor is fixed at value a, the rest of the job is: find two other numbers that add up to -a, so that all three together make zero. But "find a pair with a given target sum" is exactly Two Sum II — and we solved that with the converging two-pointer walk, as long as the array is sorted. So the plan writes itself: sort the array once, then let each position take a turn as the anchor, and for each anchor run the inward two-pointer search over the part of the array to its right.

python
nums.sort()
res = []
for i in range(len(nums) - 2):
    if i > 0 and nums[i] == nums[i-1]:
        continue                       # skip a repeated anchor (see below)
    left, right = i + 1, len(nums) - 1
    while left < right:
        s = nums[i] + nums[left] + nums[right]
        if s == 0:
            res.append([nums[i], nums[left], nums[right]])
            while left < right and nums[left] == nums[left+1]:
                left += 1              # skip repeated left values
            while left < right and nums[right] == nums[right-1]:
                right -= 1             # skip repeated right values
            left += 1
            right -= 1
        elif s < 0:
            left += 1                  # sum too small — grow it
        else:
            right -= 1                 # sum too big — shrink it
return res

The inner while loop is Two Sum II unchanged: a too-small sum steps left up to a bigger number, a too-big sum steps right down to a smaller one, and the same safety argument carries over — because the sub-array is sorted, each move throws away a number that provably cannot complete this anchor's pair.

Sorting quietly does a second job: killing duplicates

We sorted for the two-pointer walk, but it also hands us duplicate removal almost for free, because equal values now sit next to each other. There are two places a repeat can sneak in, and each has a one-line guard:

Crucial Noteafter finishing an anchor, if the next value is identical we skip it (nums[i] == nums[i-1]) — reusing the same anchor value would only rediscover the exact same triplets. And right after recording a match, we slide left and right past any equal neighbors — otherwise the very next step would find the same pair of values again at a shifted position. Both guards work only because sorting put equal values side by side; on an unsorted array you would instead need a hash set of seen triplets, which costs extra memory and bookkeeping.
Worked example:nums = [-1, 0, 1, 2, -1, -4]
- Sort first: [-4, -1, -1, 0, 1, 2].
- Anchor = -4 (index 0). Need a pair summing to 4. left=1(-1), right=5(2): -4 + -1 + 2 = -3, too small, so left steps up. It never reaches 4 — no triplet uses -4.
- Anchor = -1 (index 1). Need a pair summing to 1. left=2(-1), right=5(2): -1 + -1 + 2 = 0. Record [-1, -1, 2]. Slide inward: left=3(0), right=4(1): -1 + 0 + 1 = 0. Record [-1, 0, 1]. Pointers meet — this anchor is done.
- Anchor = -1 (index 2). Same value as the previous anchor, so skip it — it would only re-find [-1, -1, 2] and [-1, 0, 1].
- The remaining anchors have no valid pair to their right. Final: [[-1, -1, 2], [-1, 0, 1]].
The takeaway for the family

The move here is worth keeping on its own: to find k numbers with a target sum, sort once, pin down k−2 of them with outer loops, and let the converging two-pointer walk handle the final pair. 3Sum is that recipe with k = 3 (one anchor); 4Sum is k = 4 (two nested anchors), and the idea keeps going. The cost is O(N²): N choices of anchor times an O(N) inner walk, and the one-time sort is only O(N log N), which disappears next to N². Whenever a problem says "find several numbers that add to a target" and warns about duplicates, reach for sort, then converge — the ordering pays for both the search and the de-duplication at once.

Interactive Strategy Visualization

3Sum Strategy

Fix one, finding pair with Two Pointers

PIVOT
-4
0
L
-1
1
-1
2
0
3
1
4
R
2
5
-4+-1+2=-3
Fix -4. Target: 4. Left: -1, Right: 2. Sum: -3. Too small.
O(N³) Brute Force
O(N²) Time · O(N) Space Hash Set
O(N²) Time · O(1) Space Sort + Converging Scan