3Sum

Fix one number, then two-pointer the rest for its negation.

Approach

Sort the array. For each index i, use two pointers on the subarray to its right to find pairs summing to −nums[i]. Skip duplicate values for both the fixed element and the pointers to avoid repeated triples. Sorting makes both the search and dedupe straightforward.

Time complexity

O(n²)

Space complexity

O(1) or O(n) for the sort

Common mistake

Producing duplicate triples — you must skip equal adjacent values at every level, not just the outer loop.

Interactive walkthrough — step through it below, free and without an account

Problem

Given an integer array, return all distinct triples of values whose sum is exactly zero. A triple may use three different positions, and the answer must not contain duplicate combinations even when the input contains repeated values. The order of triples and the order of values inside a triple do not matter.

Examples

Input: nums = [-1, 0, 1, 2, -1, -4]

Output: [[-1, -1, 2], [-1, 0, 1]]

The two unique triples that sum to zero are (-1, -1, 2) and (-1, 0, 1).

Input: nums = [0, 0, 0, 1, -1]

Output: [[-1, 0, 1], [0, 0, 0]]

Repeated zero values can form one unique triple, while -1, 0, and 1 form the other.

Problem

Problem

Given an integer array, return all distinct triples of values whose sum is exactly zero. A triple may use three different positions, and the answer must not contain duplicate combinations even when the input contains repeated values. The order of triples and the order of values inside a triple do no…

Problem and Goal

Learning goal: Translate 3Sum into a sequence of sorted two-sum searches while preserving only unique triples.

Core idea A brute-force solution checks every choice of three indices, which takes cubic time. Instead, we sort the values and make one index responsible for the first value of a triple. The remaining two values must add to the negation of that fixed value, so the problem becomes a two-pointer search on a sorted suffix.

Definition

A valid result is a value triple such as [-1, 0, 1], not a particular ordering of its indices.

Algorithm plan

  • Sort nums in nondecreasing order.
  • Fix each candidate nums[i] as the first value.
  • Search between left = i + 1 and right = n - 1.
  • Move pointers according to whether the current sum is too small or too large.
  • Skip equal neighboring values at the fixed position and after finding a pair.

Micro-example For [-1, 0, 1, 2, -1, -4], sorting gives [-4, -1, -1, 0, 1, 2]. Fixing -1 means the other two values must sum to 1.

Pitfalls

  • Checking triples before sorting makes pointer movement invalid.
  • Returning index triples instead of value triples can obscure duplicates.
  • Stopping after the first answer misses other pairs for the same fixed value.

Recap

  • Sort first to create order and expose duplicates.
  • Fix one value and solve a two-sum suffix.
  • Uniqueness is part of the algorithm, not a cleanup step.

Problem and Goal

Use `left` and `right` to find two values summing to `-nums[i]`.

`0 + 1 = 1` completes `[-1, 0, 1]`.

Why Sorting Enables Two Pointers

Learning goal: Explain why pointer movement is safe after sorting and how the target is derived.

Core idea Once the array is sorted, increasing left increases the pair sum, while decreasing right decreases it. This lets us eliminate many candidate pairs without revisiting them. For a fixed value nums[i], the required pair target is -nums[i], because nums[i] + nums[left] + nums[right] = 0.

Key insight

The fixed value converts 3Sum into two-sum: search for nums[left] + nums[right] = -nums[i].

Pointer rules

ComparisonMeaningAction
sum < 0Pair is too smallMove left rightward
sum > 0Pair is too largeMove right leftward
sum == 0Triple is validRecord it, then skip duplicates

Because the suffix is sorted, these movements never discard a possible solution incorrectly. After recording a triple, moving both pointers also avoids reusing the same pair positions.

Micro-example With fixed -4, the target pair sum is 4. The pair (-1, 2) sums to 1, which is too small, so left must move to a larger value.

Pitfalls

  • Moving the wrong pointer can skip valid pairs.
  • Comparing only the total to zero is not enough; the array must be sorted.
  • The target changes whenever the fixed index changes.

Recap

  • Sorted order gives monotonic pointer movement.
  • Smaller sums require moving left; larger sums require moving right.
  • Every fixed value has its own pair target.

Why Sorting Enables Two Pointers

Sorted order does the work

If `nums[left] + nums[right]` is too small, moving `left` right increases the sum. If it is too large, moving `right` left decreases the sum.

Each comparison eliminates a whole group of impossible pairs.

Algorithm and Deduplication

Learning goal: Build the complete 3Sum algorithm, including duplicate handling at every level.

Core idea The outer loop chooses the first value, and the inner loop scans the suffix with two pointers. Sorting makes duplicate values adjacent, so equal candidates can be skipped locally instead of storing every result in a set. This is both simpler and more efficient in practice.

Algorithm plan

  • Sort nums.
  • For each i from 0 through n - 3:
    • If i > 0 and nums[i] == nums[i - 1], skip this fixed value.
    • Set left = i + 1 and right = n - 1.
    • While left < right, compute the three-value sum.
    • Move left or right based on the sum.
    • When the sum is zero, record the triple and skip equal values from both sides.
python
nums.sort()
for i in range(len(nums) - 2):
    if i > 0 and nums[i] == nums[i - 1]:
        continue
    left, right = i + 1, len(nums) - 1
    while left < right:
        total = nums[i] + nums[left] + nums[right]
        if total < 0: left += 1
        elif total > 0: right -= 1
        else:
            answer.append([nums[i], nums[left], nums[right]])
            left += 1; right -= 1

Micro-example After finding [-1, -1, 2], move both pointers. If the next left value equals the old left value, skip it before searching again.

Pitfalls

  • Skipping duplicates only for i still allows repeated triples from left or right.
  • Skip the outer duplicate only when i > 0.
  • The inner loop must stop when left meets right.

Recap

  • Three dedupe opportunities matter: fixed value, left value, and right value.
  • Sorting supports both pointer movement and adjacent duplicate skipping.
  • The result is accumulated directly in sorted triple order.

Algorithm and Deduplication

ApproachTimeExtra spaceTradeoff
Three nested loopsO(n^3)O(1)Checks every triple
Fix + hash setO(n^2)O(n)Needs per-iteration dedupe
Sort + two pointersO(n^2)O(1) or O(n)Order handles search and dedupe

End-to-End Walkthrough

Learning goal: Trace every meaningful pointer decision on one complete input.

Core idea Use the example [-1, 0, 1, 2, -1, -4] from the problem statement. After sorting, the array is [-4, -1, -1, 0, 1, 2]. We process each outer index, record both valid triples, and explicitly skip the repeated fixed -1.

Trace plan

  • i = 0, fixed -4: every pair is too small, so left advances until the search ends.
  • i = 1, fixed -1: pair target is 1; (-1, 2) and (0, 1) both succeed.
  • i = 2, fixed -1: skip it because the same fixed value was already processed.
  • i = 3, fixed 0: the remaining pair is too large, so right moves left.

Why it works

At each pointer position, the sorted order tells us which direction can move the sum toward zero.

Micro-example For i = 1, left = 2, and right = 5, the values are -1 and 2; together with the fixed -1, the total is zero. After recording that triple, both pointers move inward to search for a different pair.

Pitfalls

  • The second -1 at index 2 must not launch a duplicate search.
  • After [-1, -1, 2], do not leave a pointer on an equal neighboring value.
  • A failed outer iteration is still useful: it proves its suffix has no valid pair.

Recap

  • The sorted example produces exactly [-1, -1, 2] and [-1, 0, 1].
  • Pointer movement finds pairs without nested enumeration.
  • Outer-level skipping prevents the same value triple from being rediscovered.

End-to-End Walkthrough

Traces the sorted example from the first fixed value through the final answer.

Two pointers
1 / 12
Fix -4; pair (-1, 2) makes the total -3, so the pair is too small.

Array

-4-1L-1012R
left=1right=5Action: check
A sum below zero needs a larger left value.All possible pairs for fixed index 0 lie inside the current window.
1 / 12

Pitfalls and Edge Cases

Learning goal: Diagnose duplicate-output bugs and recognize the boundary cases that the sorted algorithm handles naturally.

Core idea The most common mistake is producing duplicate triples. Skipping equal values only for the outer index is insufficient: after a match, equal adjacent values at left or right can recreate the same value combination. Deduplication must happen at every level where a candidate value is chosen.

Duplicate checklist

  • Outer level: skip nums[i] when it equals nums[i - 1].
  • After a match: advance left while it equals its previous value.
  • After a match: retreat right while it equals its previous value.
  • Never skip arbitrary values before checking their sum; only equal neighbors after sorting are safe to skip.

Common pitfall

Producing duplicate triples is an algorithm bug. You must skip equal adjacent values for the fixed element and both pointers, not just the outer loop.

Micro-example In [-1, -1, 0, 1, 2], fixing the first -1 can find [-1, 0, 1]. The second -1 must be skipped, or that same value triple appears again.

Pitfalls

  • Arrays with fewer than three values return no triples.
  • An all-zero array should return exactly [[0, 0, 0]].
  • Negative values are not special; sorting and comparisons work uniformly.
  • If the fixed value is positive, the remaining sorted values are also positive, so the loop can optionally break early.

Recap

  • Adjacent equality is meaningful only because the array is sorted.
  • Deduplicate fixed values and matched pointer values.
  • Empty results and all-duplicate inputs require no special data structure.

Pitfalls and Edge Cases

After sorting, which duplicate values must be skipped to avoid repeated triples?

Complexity and Wrap-up

Learning goal: Justify the quadratic running time and distinguish algorithmic space from the sort's implementation space.

Core idea Sorting costs O(n log n). The outer loop runs O(n) times, and each two-pointer scan moves inward at most O(n) times, so the search phase costs O(n²). The quadratic phase dominates the sort for the stated asymptotic bound.

Complexity accounting

ComponentCostReason
SortO(n log n)Orders values and groups duplicates
Outer loopO(n) iterationsFixes one candidate value
Inner scansO(n) per fixed valueEach pointer moves inward only
Total timeO(n²)Quadratic search dominates
Extra spaceO(1) or O(n)Depends on sort implementation

Micro-example For six values, sorting is small, but the important scaling fact is that each fixed index performs one linear scan rather than a quadratic pair enumeration.

Why it works

Every pair is considered through a monotonic pointer scan, and sorted adjacency makes duplicate value combinations easy to skip.

Pitfalls

  • Do not call the solution O(n log n); the repeated suffix scans make it O(n²).
  • In-place sorting can use O(1) auxiliary space, but some language libraries allocate O(n).
  • Output storage is not counted as auxiliary working space.

Recap

  • Time: O(n²) after sorting.
  • Auxiliary space: O(1) with an in-place sort, or O(n) when the sort needs storage.
  • The reusable pattern is: sort, fix one value, scan two pointers, and deduplicate every level.

Complexity and Wrap-up

Summarizes the cost of sorting and the nested fixed-value plus two-pointer search.

Complexity analysis
Time
O(n^2)
Space
O(1) or O(n)
Where the time goes
  • Sort the arrayCreates order for pointer movement and adjacent deduplication.O(n log n)
  • Fix each first valueThe outer loop considers each possible fixed index.O(n)
  • Scan each suffix with two pointersAcross all fixed values, pointer scans dominate the runtime.dominantO(n^2)
How it grows
input size n →opsO(n²)O(n log n)O(n)O(log n)O(1)
Roughly what that costs
n101001,00010,000
O(n²)10010K1M100M
O(n)101001K10K

The quadratic two-pointer search is efficient enough for the standard input limit, while sorting also makes uniqueness straightforward.

Now try it on your own problem

Paste any LeetCode problem — or one of your own — and codebrew builds a walkthrough like this one for it.

Build a lesson from any problem

Related problems