3Sum
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.
- 3 <= nums.length <= 3000
- Solution set must not contain duplicate triplets
- -10⁵ <= nums[i] <= 10⁵
nums = [-1,0,1,2,-1,-4][[-1,-1,2],[-1,0,1]]nums = [0,0,0][[0,0,0]]nums = [1,2,-2,-1][]Finding three numbers that sum to zero is fundamentally about reducing a three-variable search into a simpler, two-variable one. By anchoring one element, we transform the problem into a search for a pair that completes the zero-sum, which is much easier to manage in a sorted environment.
The most basic way to solve this is to pick every possible triplet (i, j, k) and check if their sum is zero. This involves three nested loops, which is incredibly inefficient. As the number of elements grows, this approach quickly hits a wall and becomes computationally impossible for larger datasets.
# Brute force: nested loops
def brute_force(nums):
res = set()
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:
res.add(tuple(sorted([nums[i], nums[j], nums[k]])))
return list(res)We could fix one anchor nums[i] and then treat the remaining problem as a "Two Sum" search. We could use a hash set to track seen numbers. This is faster than brute force, but hash sets make it surprisingly difficult to handle duplicate triplets in the final output without creating extra sets or sorting the result, which adds significant overhead and complexity to the code.
The true insight here is to use a sorted array. Once the array is sorted, we fix one element as the Anchor, and then use the Two-Pointer Pattern on the rest of the array. The sorted order allows us to not only converge on the answer in O(N) time but also makes it trivially easy to skip duplicates simply by ignoring adjacent numbers that have the same value.
We iterate through the sorted array, using each element as an anchor. For each anchor, we set left and right pointers on the remaining subarray to find pairs that sum to -anchor.
- If the sum is too small, we need a larger value (move left forward).
- If the sum is too large, we need a smaller value (move right backward).
- If we find a match, we store it and move both pointers inward, while skipping any duplicates to maintain uniqueness.
# Optimal: anchor + two pointers
def three_sum(nums):
nums.sort()
res = []
for i in range(len(nums) - 2):
if i > 0 and nums[i] == nums[i-1]: continue
l, r = i + 1, len(nums) - 1
while l < r:
s = nums[i] + nums[l] + nums[r]
if s == 0:
res.append([nums[i], nums[l], nums[r]])
while l < r and nums[l] == nums[l+1]: l += 1
while l < r and nums[r] == nums[r-1]: r -= 1
l += 1; r -= 1
elif s < 0: l += 1
else: r -= 1
return res3Sum Strategy
Fix one, finding pair with Two Pointers
Key Insight
Sorting transforms the problem. By fixing one element nums[i], we reduce the problem to finding two numbers that sum to -nums[i], which is exactly Two Sum II.
Handling Duplicates
We skip duplicate values for both the pivot and the two pointers to ensure uniqueness of the triplets without using a Set.