How to Explain 3Sum in a Coding Interview (Step-by-Step)
3Sum is where two-pointer technique graduates from beginner to intermediate. The core algorithm is only a few lines — but the duplicate-skipping logic is what trips up most candidates. Get that right and the rest of the explanation writes itself.
In this post, you’ll learn exactly how to walk an interviewer through LeetCode 15: why you sort first, how the fix-one + two-pointer reduction works, where duplicates sneak in, and how to handle all three skip points cleanly.
Problem Statement
Given an integer array nums, return all the triplets [nums[i], nums[j], nums[k]] such that i != j, i != k, j != k, and nums[i] + nums[j] + nums[k] == 0.
The solution set must not contain duplicate triplets.
Examples
Output: [[-1, -1, 2], [-1, 0, 1]]
Output: []
Output: [[0, 0, 0]]
Constraints
3 <= nums.length <= 3000-105 <= nums[i] <= 105
What Is the Problem?
“I need to find every unique set of three numbers in the array that sums to zero. The tricky part isn’t the sum — it’s the word ‘unique’. The input may contain duplicates, and I can’t return the same triplet twice even if it can be formed from different indices.”
Calling out the uniqueness constraint upfront shows the interviewer you’ve identified the hardest part of the problem before writing a single line of code.
Step 1: Compare Approaches Before Jumping to Code
“Both the HashSet and two-pointer approaches are O(n²), but sorting makes deduplication effortless — once the array is sorted, duplicate values are adjacent, so skipping them is a simple while-loop. That’s why sort + two pointers is the clean interview answer.”
The Key Insight — Reduce to 2Sum
Sort the array. Then iterate with index i, fixing nums[i] as the first element of the triplet. For each fixed element, the problem reduces to finding two numbers in the remaining subarray that sum to -nums[i] — which is exactly Two Sum II, solvable in O(n) with two pointers. Outer loop O(n) × inner two-pointer O(n) = O(n²) total.
“Sorting unlocks two things: it lets me use two pointers for the inner loop, and it puts duplicates side-by-side so I can skip them with a simple comparison. Both properties are essential.”
Step 2: Explain the Algorithm in Plain English
Sort nums in ascending order. This costs O(n log n) but is dominated by the O(n²) main loop. It also makes duplicate detection trivial.
iLoop i from 0 to n - 3. If nums[i] > 0, break early — a sorted array means no triplet can sum to zero with a positive first element. If nums[i] == nums[i-1], skip to avoid duplicate triplets at the i level.
Set left = i + 1 and right = n - 1. Compute the sum nums[i] + nums[left] + nums[right]. If the sum is too small, advance left. If too large, retreat right. If exactly zero, record the triplet.
After recording a triplet, advance left past any repeated value, and retreat right past any repeated value. Then move both pointers one more step inward to continue the search.
Step-by-Step Visual — nums = [-4, -1, -1, 0, 1, 2]
After sorting. Let’s trace the first iteration where i = 1 (nums[i] = -1), target = 1.
The Duplicate-Skip Logic — The Part Everyone Trips Over
There are three places where duplicates must be skipped. Miss any one and the output contains duplicate triplets.
1. Skip duplicate i values — at the start of the outer loop, if nums[i] == nums[i-1] (and i > 0), skip. Same first element would produce the same triplets again.
2. Skip duplicate left values after a match — after recording a triplet, advance left while nums[left] == nums[left-1]. The same second element would pair with the same third element.
3. Skip duplicate right values after a match — similarly, retreat right while nums[right] == nums[right+1]. Same reasoning applies to the third element.
“The key is that skipping happens after recording the triplet — not before. And thei > 0guard on the outer skip is critical: without it, we’d skip index 0 when comparing to a non-existentnums[-1].”
Step 3: Talk Through the Code
class Solution:
def threeSum(self, nums: List[int]) -> List[List[int]]:
nums.sort() # Sort once — enables two pointers + easy deduplication
result = []
for i in range(len(nums) - 2):
# Early exit: smallest remaining value is positive → no triplet can sum to 0
if nums[i] > 0:
break
# Skip duplicate values for the fixed pointer
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 # Sum too small — move left pointer right
elif total > 0:
right -= 1 # Sum too large — move right pointer left
else:
result.append([nums[i], nums[left], nums[right]])
# Skip duplicate values for left and right after a match
while left < right and nums[left] == nums[left + 1]:
left += 1
while left < right and nums[right] == nums[right - 1]:
right -= 1
left += 1 # Move both inward to look for the next triplet
right -= 1
return result
Draw attention to the early-break optimisation — once nums[i] > 0, the sorted array guarantees nums[left] and nums[right] are also positive, so the sum can never be zero. This doesn't change worst-case complexity but significantly improves real-world performance.
Step 4: Highlight Edge Cases
"After sorting,nums[i] = 0,leftandrightboth point to 0. Sum is 0 — we record[0,0,0]. After recording, both duplicate-skip loops don't advance (no adjacent duplicates to skip past), then both pointers move inward and cross. One triplet returned — correct."
"Ati = 0,nums[i] = 1 > 0so we break immediately. The result is an empty list — correct."
"After sorting, we fix-2, then two pointers find[0, 2]. After recording[-2, 0, 2], the skip logic advancesleftpast the second0and retreatsrightpast the second2. Pointers then cross — only one triplet returned, no duplicates."
"The minimum input is three elements. The outer loop runs once with i = 0. If the three elements sum to zero we return that triplet; otherwise we return an empty list. No special casing needed."
"Sum is -3, never 0. The two-pointer loop terminates without recording anything. The result is an empty list — correct."
Step 5: State Time and Space Complexity
| Dimension | Complexity | Reason |
|---|---|---|
| Time | O(n²) | Sorting is O(n log n); outer loop O(n) × inner two-pointer O(n) dominates at O(n²) |
| Space | O(n) | O(log n) for the sort stack; output list can hold O(n) triplets in the worst case — output space is typically excluded from analysis |
"Sorting costs O(n log n) but the nested loop is O(n²), so sorting doesn't affect the overall time complexity. For space, if we exclude the output list (standard convention), it's O(log n) for the sort. If we include the output, it's O(n) in the worst case — for example, an array of all zeros of length n would produce O(n) triplets."
Interview Checklist for 3Sum
- Restate — "find all unique triplets that sum to zero; no duplicate triplets in the output"
- Call out the uniqueness constraint immediately — it's the hardest part
- Mention brute force O(n³) → explain why it's rejected
- Explain why sorting enables the two-pointer approach and trivial deduplication
- State the reduction: fix
nums[i], reduce to Two Sum II in the subarray - Explain all three duplicate-skip points: outer
i, innerleft, innerright - Mention the
i > 0guard on the outer skip - Mention the early-break when
nums[i] > 0 - Call out edge cases: all zeros, no valid triplet, minimum length input
- State O(n²) time and O(n) space (or O(log n) excluding output)
Related Problems to Practice
If you can solve 3Sum cleanly, try these next:
right after a match. Before finishing, always trace through [0, 0, 0] and [-2, 0, 0, 2, 2] mentally to confirm your deduplication handles both cases without producing repeats.
