How to Explain 3Sum in a Coding Interview (Step-by-Step)

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

LeetCode 15 Medium

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

Example 1
Input: nums = [-1, 0, 1, 2, -1, -4]
Output: [[-1, -1, 2], [-1, 0, 1]]
Example 2
Input: nums = [0, 1, 1]
Output: []
Example 3
Input: nums = [0, 0, 0]
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

✗ Too Slow — O(n³)
Brute Force
Three nested loops checking every combination. Correct but O(n³) — fails for n = 3000.
~ Possible — O(n²)
HashSet Fix + Two Sum
Fix one element, use a HashSet for the two-sum complement. O(n²) time but deduplication with a set of tuples adds complexity.
✓ Optimal — O(n²)
Sort + Two Pointers
Sort first, fix one element, run two pointers inward. O(n²) time, O(1) extra space, and deduplication is trivial with sorted order.
“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

💡 Core Insight

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

1 Sort the array

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.

2 Iterate with a fixed pointer i

Loop 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.

3 Run two pointers on the subarray

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.

4 Skip duplicates after a match

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.

0
i=1
L=2
3
4
R=5
Initial
-4
-1
-1
0
1
2
sum = -1 + (-1) + 2 = 0 ✓ → record [-1,-1,2]
0
i=1
L=3
R=4
Skip dups
-4
-1
0
1
sum = -1 + 0 + 1 = 0 ✓ → record [-1,0,1]
0
i=1
L=R
Pointers cross
-4
-1
1
L ≥ R → inner loop ends for i=1
Fixed element (i)
Left pointer
Right pointer
Triplet found
Skipped (duplicate or passed)

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.

⚠️ Three Skip Points

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 the i > 0 guard on the outer skip is critical: without it, we’d skip index 0 when comparing to a non-existent nums[-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

1 All zeros — [0, 0, 0]
"After sorting, nums[i] = 0, left and right both 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."
2 No valid triplet — [1, 2, 3]
"At i = 0, nums[i] = 1 > 0 so we break immediately. The result is an empty list — correct."
3 Many duplicates — [-2, 0, 0, 2, 2]
"After sorting, we fix -2, then two pointers find [0, 2]. After recording [-2, 0, 2], the skip logic advances left past the second 0 and retreats right past the second 2. Pointers then cross — only one triplet returned, no duplicates."
4 Array length exactly 3
"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."
5 All same negative number — [-1, -1, -1]
"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

DimensionComplexityReason
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, inner left, inner right
  • Mention the i > 0 guard 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)

If you can solve 3Sum cleanly, try these next:

💡 The most common mistake in 3Sum is forgetting one of the three duplicate-skip points — especially the skip for 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.
Scroll to Top