Skip to main content

3Sum

How to reduce a three-variable problem into two pointers after sorting and handling duplicates in a controlled way.

Given an integer array nums, return all triplets [nums[i], nums[j], nums[k]] such that:

  • i != j
  • i != k
  • j != k
  • nums[i] + nums[j] + nums[k] === 0

The solution set must not contain duplicate triplets.

Example:

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

What to notice before coding

Common mistakes

Step 1 — Write the mental baseline without running from it

The first natural read is:

  • choose a first number
  • choose a second
  • choose a third
  • test whether they sum to zero

That becomes three loops.

Correct. But too expensive.

Step 2 — Look for a reduction

If you fix nums[i], you are no longer searching for three numbers.

Now you only need two numbers in the remaining range that satisfy:

nums[left] + nums[right] = -nums[i]

That already looks like Two Sum.

Step 3 — Understand why sorting helps

After sorting:

  • if the sum is too small, you need a bigger value
  • if the sum is too large, you need a smaller value

That is what allows left and right to work.

Step 4 — Remember that duplicates are part of the problem

Solving the sum is not enough.

After finding a valid triplet, you need to skip repeated values so you do not generate the same answer again.

The interactive editor needs JavaScript. You can still read the prompt and copy the starter code below.

Starter code

export function threeSum(nums: number[]): number[][] {
  // your solution here
  return []
}
solution.ts
Stuck? Hints available.

Haven't solved it yet?

Viewing the solution now may reduce learning. It's worth trying a bit more.

Open the reference solution

Without JavaScript, the reference solution is shown inline instead of in a dialog.

Solution

Final complexity

Time: O(n²)

Space: O(1)

Solution 1 — Three loops O(n³)

Good baseline to show the raw problem.

export function threeSum(nums: number[]): number[][] {
  const result: number[][] = []
  const seen = new Set<string>()

  for (let i = 0; i < nums.length; i += 1) {
    for (let j = i + 1; j < nums.length; j += 1) {
      for (let k = j + 1; k < nums.length; k += 1) {
        if (nums[i] + nums[j] + nums[k] === 0) {
          const triplet = [nums[i], nums[j], nums[k]].sort((a, b) => a - b)
          const key = triplet.join(',')

          if (!seen.has(key)) {
            seen.add(key)
            result.push(triplet)
          }
        }
      }
    }
  }

  return result
}

It works, but it is far too heavy.

Solution 2 — Sort + two pointers O(n²)

Now the three-number problem becomes a repeated two-number problem for each fixed index.

export function threeSum(nums: number[]): number[][] {
  const sorted = [...nums].sort((a, b) => a - b)
  const result: number[][] = []

  for (let index = 0; index < sorted.length - 2; index += 1) {
    const value = sorted[index]

    if (index > 0 && value === sorted[index - 1]) {
      continue
    }

    let left = index + 1
    let right = sorted.length - 1

    while (left < right) {
      const sum = value + sorted[left] + sorted[right]

      if (sum === 0) {
        result.push([value, sorted[left], sorted[right]])
        left += 1
        right -= 1

        while (left < right && sorted[left] === sorted[left - 1]) {
          left += 1
        }

        while (left < right && sorted[right] === sorted[right + 1]) {
          right -= 1
        }

        continue
      }

      if (sum < 0) {
        left += 1
      } else {
        right -= 1
      }
    }
  }

  return result
}

The important step is not sorting by itself. It is what sorting lets you do afterward.

What to say in the interview

A strong short explanation would be:

The baseline tests all triplets and costs O(n³). The better version sorts the array, fixes one element, and reduces the rest to a two pointers problem. That lets me adjust the sum by moving left and right, while also handling duplicates during the pass.

That shows that you:

  • decomposed the problem correctly
  • connected 3Sum to a pattern you already knew
  • explained deduplication as part of the rule, not a random detail

When this pattern shows up again

The same pattern comes back when you need to:

  • fix part of the answer and solve the rest
  • combine sorting with two pointers
  • control duplicates in combination problems
  • reduce search dimension with structure

The point is not memorizing 3Sum.

The point is learning how to reduce a bigger problem into a smaller familiar one.

Next challenge Container With Most Water Previous challenge Longest Substring Without Repeating Characters

Related challenges

Related articles