15. 3Sum - Explanation

Problem Link

Description

Given an integer array nums, return all the triplets [nums[i], nums[j], nums[k]] where nums[i] + nums[j] + nums[k] == 0, and the indices i, j and k are all distinct.

The output should not contain any duplicate triplets. You may return the output and the triplets in any order.

Example 1:

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

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

Explanation:
nums[0] + nums[1] + nums[2] = (-1) + 0 + 1 = 0.
nums[1] + nums[2] + nums[4] = 0 + 1 + (-1) = 0.
nums[0] + nums[3] + nums[4] = (-1) + 2 + (-1) = 0.
The distinct triplets are [-1,0,1] and [-1,-1,2].

Example 2:

Input: nums = [0,1,1]

Output: []

Explanation: The only possible triplet does not sum up to 0.

Example 3:

Input: nums = [0,0,0]

Output: [[0,0,0]]

Explanation: The only possible triplet sums up to 0.

Constraints:

  • 3 <= nums.length <= 1000
  • -10^5 <= nums[i] <= 10^5


Topics

Recommended Time & Space Complexity

You should aim for a solution with O(n^2) time and O(1) space, where n is the size of the input array.


Hint 1

A brute force solution would be to check for every triplet in the array. This would be an O(n^3) solution. Can you think of a better way?


Hint 2

Can you think of an algorithm after sorting the input array? What can we observe by rearranging the given equation in the problem?


Hint 3

We can iterate through nums with index i and get nums[i] = -(nums[j] + nums[k]) after rearranging the equation, making -nums[i] = nums[j] + nums[k]. For each index i, we should efficiently calculate the j and k pairs without duplicates. Which algorithm is suitable to find j and k pairs?


Hint 4

To efficiently find the j and k pairs, we run the two pointer approach on the elements to the right of index i as the array is sorted. When we run two pointer algorithm, consider j and k as pointers (j is at left, k is at right) and target = -nums[i], if the current sum num[j] + nums[k] < target then we need to increase the value of current sum by incrementing j pointer. Else if the current sum num[j] + nums[k] > target then we should decrease the value of current sum by decrementing k pointer. How do you deal with duplicates?


Hint 5

When the current sum nums[j] + nums[k] == target add this pair to the result. We can move j or k pointer until j < k and the pairs are repeated. This ensures that no duplicate pairs are added to the result.


Company Tags

Please upgrade to NeetCode Pro to view company tags.



Prerequisites

Before attempting this problem, you should be comfortable with:

  • Sorting - Sorting the array enables efficient duplicate handling and two-pointer technique
  • Two pointers - The optimal solution uses two pointers to find pairs that sum to a target
  • Hash maps - An alternative approach uses hash maps for O(1) lookups
  • Handling duplicates - Skipping duplicate values to avoid repeated triplets in the result

1. Brute Force

Intuition

The brute-force approach simply tries every possible triplet.
Since we check all combinations (i, j, k) with i < j < k, we are guaranteed to find all sets of three numbers that sum to zero.
Sorting helps keep the triplets in order and makes it easier to avoid duplicates by storing them in a set.

Algorithm

  1. Sort the array to make handling duplicates easier.
  2. Create an empty set res to store unique triplets.
  3. Use three nested loops:
    • For each i,
    • For each j > i,
    • For each k > j,
      • Check if nums[i] + nums[j] + nums[k] == 0.
      • If true, add the sorted triplet to the set.
  4. Convert the set of tuples back into a list of lists.
  5. Return the result.
class Solution:
    def threeSum(self, nums: List[int]) -> List[List[int]]:
        res = set()
        nums.sort()
        for i in range(len(nums)):
            for j in range(i + 1, len(nums)):
                for k in range(j + 1, len(nums)):
                    if nums[i] + nums[j] + nums[k] == 0:
                        tmp = [nums[i], nums[j], nums[k]]
                        res.add(tuple(tmp))
        return [list(i) for i in res]

Time & Space Complexity

  • Time complexity: O(n3)O(n ^ 3)
  • Space complexity: O(m)O(m)

Where mm is the number of triplets and nn is the length of the given array.


2. Hash Map

Intuition

After sorting the array, we can fix two numbers and look for the third number that completes the triplet.
To do this efficiently, we use a hash map that stores how many times each number appears.
As we pick the first and second numbers, we temporarily reduce their counts in the map so we don't reuse them.
Then we check whether the needed third value still exists in the map.
Sorting also helps us skip duplicates easily so we only add unique triplets.

Algorithm

  1. Sort the array to organize duplicates and allow easy skipping.
  2. Build a frequency map count for all numbers.
  3. Initialize an empty list res for storing valid triplets.
  4. Loop through each index i:
    • Decrease the count of nums[i] (so it won't be reused).
    • Skip duplicates of the first element.
    • Loop through each index j > i:
      • Decrease the count of nums[j].
      • Skip duplicates of the second element.
      • Compute the needed third value:
        target = -(nums[i] + nums[j])
      • If target still has a positive count, add the triplet.
    • After finishing all js, restore the counts for the second loop by adding back the decremented values.
  5. Return res containing all found triplets.
class Solution:
    def threeSum(self, nums: List[int]) -> List[List[int]]:
        nums.sort()
        count = defaultdict(int)
        for num in nums:
            count[num] += 1

        res = []
        for i in range(len(nums)):
            count[nums[i]] -= 1
            if i and nums[i] == nums[i - 1]:
                continue

            for j in range(i + 1, len(nums)):
                count[nums[j]] -= 1
                if j - 1 > i and nums[j] == nums[j - 1]:
                    continue
                target = -(nums[i] + nums[j])
                if count[target] > 0:
                    res.append([nums[i], nums[j], target])

            for j in range(i + 1, len(nums)):
                count[nums[j]] += 1
        return res

Time & Space Complexity

  • Time complexity: O(n2)O(n ^ 2)
  • Space complexity: O(n)O(n)

3. Two Pointers

Intuition

After sorting the array, we can fix one number and then search for the other two using the two-pointer technique.
Sorting helps in two ways:

  1. It lets us skip duplicates easily.
  2. It ensures that moving the left or right pointer will increase or decrease the sum in a predictable way.

For each fixed number a, we place two pointers:

  • l starts just after i,
  • r starts at the end.

If the current sum is too large, we move r left to reduce it.
If the sum is too small, we move l right to increase it.
When the sum is exactly zero, we record the triplet and skip duplicates.

Algorithm

  1. Sort the array to handle duplicates and enable two-pointer logic.
  2. Loop through the array using index i:
    • Let a = nums[i].
    • If a > 0, break (all remaining numbers are positive).
    • Skip duplicate values for the first number.
  3. Set two pointers:
    • l = i + 1
    • r = len(nums) - 1
  4. While l < r:
    • Compute threeSum = a + nums[l] + nums[r].
    • If threeSum > 0, move r left.
    • If threeSum < 0, move l right.
    • If threeSum == 0:
      • Add the triplet to the result.
      • Move both pointers inward.
      • Skip duplicates at the left pointer.
  5. Return the list of all valid triplets.
class Solution:
    def threeSum(self, nums: List[int]) -> List[List[int]]:
        res = []
        nums.sort()

        for i, a in enumerate(nums):
            if a > 0:
                break

            if i > 0 and a == nums[i - 1]:
                continue

            l, r = i + 1, len(nums) - 1
            while l < r:
                threeSum = a + nums[l] + nums[r]
                if threeSum > 0:
                    r -= 1
                elif threeSum < 0:
                    l += 1
                else:
                    res.append([a, nums[l], nums[r]])
                    l += 1
                    r -= 1
                    while nums[l] == nums[l - 1] and l < r:
                        l += 1

        return res

Time & Space Complexity

  • Time complexity: O(n2)O(n ^ 2)
  • Space complexity:
    • O(1)O(1) or O(n)O(n) extra space depending on the sorting algorithm.
    • O(m)O(m) space for the output list.

Where mm is the number of triplets and nn is the length of the given array.


Common Pitfalls

Forgetting to Skip Duplicates

A common mistake is not properly skipping duplicate values, which leads to duplicate triplets in the result. After sorting, when you find a valid triplet, you must skip over all identical values for the first element (outer loop) and the left pointer. Failing to do this causes wrong answers on inputs like [-1, -1, 0, 1, 1].

Not Sorting the Array First

The two-pointer approach only works correctly on a sorted array. Without sorting, moving pointers based on sum comparisons does not guarantee you will find all valid triplets. Always sort the input array before applying the two-pointer technique.

Incorrect Early Termination

When the first element nums[i] is positive, all remaining elements are also positive (since the array is sorted), so no triplet can sum to zero. However, incorrectly breaking when nums[i] >= 0 instead of nums[i] > 0 misses cases like [0, 0, 0]. The break condition should be nums[i] > 0, not nums[i] >= 0.