3Sum

Medium Two Pointers

Problem

Given an integer array nums, return all unique triplets [nums[i], nums[j], nums[k]] such that i != j, i != k, j != k, and nums[i] + nums[j] + nums[k] == 0.

  • 0 ≤ nums.length ≤ 3000
  • −10⁵ ≤ nums[i] ≤ 10⁵

Example

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

First, the array is sorted to [-4,-1,-1,0,1,2]. The algorithm iterates through each number as a potential first element of the triplet. At index 1 (value -1), it uses two pointers starting at indices 2 and 5 to find pairs that sum with -1 to zero. The pair (nums[2], nums[5]) = (-1, 2) sums to 1, so the right pointer moves left. Eventually, the pair (-1, 2) is found, forming the triplet [-1, -1, 2]. The algorithm skips duplicates by checking if the current number equals the previous one. This process continues, finding the second triplet [-1, 0, 1]. The key is sorting and skipping duplicates to avoid repeated triplets.

Approach

Straightforward Solution

A brute-force approach would check all triplets with three nested loops, resulting in O(n^3) time complexity, which is too slow for large inputs.

Core Observation

The problem requires finding triplets that sum to zero without duplicates. Sorting the array enables efficient two-pointer scanning for pairs that complement the current element to zero, and also allows easy skipping of duplicates to ensure unique triplets.

Path to Optimal

Sorting the array allows fixing one element and searching for the other two using two pointers moving inward from both ends of the remaining array. This reduces the complexity to O(n^2). Skipping duplicates at both the fixed element and during pointer movement prevents repeated triplets.

Optimal Approach

Sort the array. Iterate through each element as the first element of the triplet, skipping duplicates. For each fixed element, use two pointers (left and right) to find pairs that sum to the negative of the fixed element. Move pointers inward based on the sum comparison, skipping duplicates to avoid repeated triplets. Collect all valid triplets in the result list.

Time

O(n^2)

Sorting takes O(n log n). The main loop runs O(n) times, and for each iteration, the two-pointer search runs in O(n), resulting in O(n^2) overall.

Space

O(n)

The sorting algorithm requires O(n) space in the worst case. The output list space is not counted as auxiliary space.

Pattern Spotlight

Two Pointers (Sorted Array Pair Search)

When searching for pairs that sum to a target in a sorted array, use two pointers starting at opposite ends and move inward based on the sum comparison; this approach efficiently finds all pairs in O(n) time and can be extended to triplets by fixing one element and searching pairs for the complement.

Solution

1class Solution:
2 def threeSum(self, nums: list[int]) -> list[list[int]]:
3 res = []
4 nums.sort()
5
6 for i, a in enumerate(nums):
7 if i > 0 and a == nums[i - 1]:
8 continue
9
10 l, r = i + 1, len(nums) - 1
11 while l < r:
12 three_sum = a + nums[l] + nums[r]
13 if three_sum > 0:
14 r -= 1
15 elif three_sum < 0:
16 l += 1
17 else:
18 res.append([a, nums[l], nums[r]])
19 l += 1
20 while l < r and nums[l] == nums[l - 1]:
21 l += 1
22 return res

Step-by-Step Solution

1

Sort the Array and Initialize Result Container

3res = []
4nums.sort()

Objective

To prepare the input for efficient two-pointer searching and store the resulting triplets.

Key Insight

Sorting the array is essential because it enables the two-pointer technique to find pairs summing to a target in linear time. Initializing the result list upfront provides a container to accumulate valid triplets.

Interview Quick-Check

Core Logic

Sorting transforms the problem into a structured search space where two pointers can efficiently find pairs summing to a target.

State & Boundaries

The result list must be initialized before iteration to collect all valid triplets.

2

Iterate Over Array, Skipping Duplicate Fixed Elements

6for i, a in enumerate(nums):
7 if i > 0 and a == nums[i - 1]:
8 continue

Objective

To select each unique element as the first element of potential triplets while avoiding duplicate work.

Key Insight

By skipping fixed elements that are the same as the previous one, the algorithm avoids generating duplicate triplets starting with the same number. This pruning is critical to ensure uniqueness and reduce unnecessary computations.

Interview Quick-Check

Core Logic

Skipping duplicates at the fixed element level prevents repeated triplets that differ only in order.

Common Pitfalls & Bugs

Failing to skip duplicates here leads to duplicate triplets in the output.

3

Use Two Pointers to Find Complementary Pairs for Each Fixed Element

10l, r = i + 1, len(nums) - 1
11while l < r:
12 three_sum = a + nums[l] + nums[r]
13 if three_sum > 0:
14 r -= 1
15 elif three_sum < 0:
16 l += 1
17 else:
18 res.append([a, nums[l], nums[r]])
19 l += 1
20 while l < r and nums[l] == nums[l - 1]:
21 l += 1

Objective

To find all pairs in the remaining array that sum with the fixed element to zero, efficiently and without duplicates.

Key Insight

The two-pointer technique exploits the sorted order to move pointers inward based on the sum comparison, eliminating the need for nested loops. After finding a valid triplet, advancing the left pointer and skipping duplicates ensures unique triplets are collected without redundant checks.

Interview Quick-Check

Core Logic

Two pointers scan from both ends towards the center, adjusting based on the sum relative to zero, to find all valid pairs in O(n) time.

State & Boundaries

The loop continues while left pointer is less than right pointer, ensuring pairs are unique and no invalid indices are accessed.

Common Pitfalls & Bugs

Not skipping duplicates after finding a valid triplet causes repeated triplets in the result.

Complexity

This approach reduces the pair search from O(n^2) to O(n) per fixed element, leading to O(n^2) overall.

4

Return the List of Unique Triplets

22return res

Objective

To output the final collection of all unique triplets found.

Key Insight

After completing the iteration and pair searches, the result list contains all valid triplets without duplicates, ready to be returned as the solution.

Interview Quick-Check

Core Logic

Returning the accumulated result completes the algorithm and provides the answer to the problem.

Line Analysis

This solution has 3 Critical lines interviewers watch for.

Line 7 Critical
if i > 0 and a == nums[i - 1]:

Skip the current element if it is the same as the previous to avoid duplicates.

Skipping duplicates at the fixed element prevents generating identical triplets multiple times, ensuring uniqueness in the result.

Line 17 Critical
else:

If the sum equals zero, a valid triplet is found.

This condition identifies the core success case where the three numbers sum to zero, ready to be recorded.

Line 20 Critical
while l < r and nums[l] == nums[l - 1]:

Skip duplicate elements at the left pointer to avoid repeated triplets.

This loop prevents adding triplets with the same second element multiple times, ensuring uniqueness.

Line 4 Core
nums.sort()

Sort the input array in ascending order.

Sorting enables the two-pointer technique by imposing order, which allows efficient pair searching and duplicate skipping.

Line 13 Core
if three_sum > 0:

If the sum is greater than zero, move the right pointer left to reduce sum.

Because the array is sorted, decreasing the right pointer moves to a smaller number, reducing the sum towards zero.

Line 15 Core
elif three_sum < 0:

If the sum is less than zero, move the left pointer right to increase sum.

Increasing the left pointer moves to a larger number, increasing the sum towards zero in a sorted array.

Line 8 Core
continue

Continue to the next iteration if duplicate fixed element detected.

This control flow enforces the duplicate skipping logic, preventing redundant computations.

Line 10 Core
l, r = i + 1, len(nums) - 1

Initialize two pointers: left just after fixed element, right at array end.

Setting pointers at these positions prepares for scanning the remaining array for pairs that complement the fixed element to zero.

Line 12 Core
three_sum = a + nums[l] + nums[r]

Calculate the sum of the fixed element and the two pointers' elements.

This sum determines whether the current triplet meets the zero-sum condition or if pointers need adjustment.

Line 18 Core
res.append([a, nums[l], nums[r]])

Append the found triplet to the result list.

Recording the valid triplet ensures it is included in the final output.

Line 19 Core
l += 1

Move the left pointer right to continue searching for other pairs.

Advancing the left pointer explores new pairs while avoiding duplicates of the current left element.

Line 11 Standard
while l < r:

Loop while left pointer is less than right pointer.

This condition ensures valid pairs are considered and terminates when pointers cross, completing the search for the current fixed element.

Line 21 Standard
l += 1

Increment the left pointer while duplicates are found.

This enforces the duplicate skipping logic after a valid triplet is recorded.

Line 6 Standard
for i, a in enumerate(nums):

Iterate over the array with index and value.

This loop selects each element as a potential first element of a triplet, setting up the two-pointer search for the other two elements.

Line 14 Standard
r -= 1

Decrement the right pointer.

This action implements the strategy to reduce the sum by moving to a smaller number on the right side.

Line 16 Standard
l += 1

Increment the left pointer.

This action implements the strategy to increase the sum by moving to a larger number on the left side.

Line 22 Standard
return res

Return the list of all unique triplets found.

This completes the algorithm by outputting the accumulated valid triplets as the solution.

Line 3 Basic
res = []

Initialize an empty list to store the resulting triplets.

This list accumulates all unique triplets found during the search, serving as the output container.

Test Your Understanding

Why is it necessary to skip duplicates both when fixing the first element and when moving the left pointer?

Skipping duplicates prevents generating identical triplets multiple times. For the fixed element, it avoids repeating the same starting point. For the left pointer, it avoids including the same pair multiple times, ensuring the result contains unique triplets.

Related Problems

Two Pointers pattern

Don't just read it. Drill it.

Reconstruct 3Sum from memory until it sticks. AlgoDrill blanks out key lines and makes you fill them back in, step by step.

Unlock the 3Sum drill

or drill a free problem