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]]
Input: nums = [0, 0, 0, 1, -1]
Output: [[-1, 0, 1], [0, 0, 0]]
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
numsin nondecreasing order. - Fix each candidate
nums[i]as the first value. - Search between
left = i + 1andright = 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
| Comparison | Meaning | Action |
|---|---|---|
sum < 0 | Pair is too small | Move left rightward |
sum > 0 | Pair is too large | Move right leftward |
sum == 0 | Triple is valid | Record 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 movingright. - 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
ifrom0throughn - 3:- If
i > 0andnums[i] == nums[i - 1], skip this fixed value. - Set
left = i + 1andright = n - 1. - While
left < right, compute the three-value sum. - Move
leftorrightbased on the sum. - When the sum is zero, record the triple and skip equal values from both sides.
- If
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 -= 1Micro-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
istill allows repeated triples fromleftorright. - Skip the outer duplicate only when
i > 0. - The inner loop must stop when
leftmeetsright.
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
| Approach | Time | Extra space | Tradeoff |
|---|---|---|---|
| Three nested loops | O(n^3) | O(1) | Checks every triple |
| Fix + hash set | O(n^2) | O(n) | Needs per-iteration dedupe |
| Sort + two pointers | O(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, soleftadvances until the search ends.i = 1, fixed-1: pair target is1;(-1, 2)and(0, 1)both succeed.i = 2, fixed-1: skip it because the same fixed value was already processed.i = 3, fixed0: the remaining pair is too large, sorightmoves 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
-1at index2must 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.
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 equalsnums[i - 1]. - After a match: advance
leftwhile it equals its previous value. - After a match: retreat
rightwhile 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
| Component | Cost | Reason |
|---|---|---|
| Sort | O(n log n) | Orders values and groups duplicates |
| Outer loop | O(n) iterations | Fixes one candidate value |
| Inner scans | O(n) per fixed value | Each pointer moves inward only |
| Total time | O(n²) | Quadratic search dominates |
| Extra space | O(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 itO(n²). - In-place sorting can use
O(1)auxiliary space, but some language libraries allocateO(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, orO(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.
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
