12. 3Sum
View on NeetCode
View on LeetCode
Problem
Given an integer array nums, return all the triplets [nums[i], nums[j], nums[k]] such that i != j, i != k, and j != k, and nums[i] + nums[j] + nums[k] == 0.
Notice that the solution set must not contain duplicate triplets.
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].
Notice that the order of the output and the order of the triplets does not matter.
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 <= 3000-10^5 <= nums[i] <= 10^5
Solution
Approach: Sort + Two Pointers
The key insight is to first sort the array, then fix one element and use two pointers to find pairs that sum to the negative of the fixed element. This reduces the 3Sum problem to multiple 2Sum problems.
To avoid duplicates, we skip over repeated values for all three positions (fixed element, left pointer, and right pointer).
Implementation
class Solution:
def threeSum(self, nums: List[int]) -> List[List[int]]:
nums.sort()
result = []
for i in range(len(nums) - 2):
# Skip duplicate values for first element
if i > 0 and nums[i] == nums[i - 1]:
continue
# Two pointers for remaining elements
left, right = i + 1, len(nums) - 1
target = -nums[i]
while left < right:
current_sum = nums[left] + nums[right]
if current_sum == target:
result.append([nums[i], nums[left], nums[right]])
# Skip duplicate values for second element
while left < right and nums[left] == nums[left + 1]:
left += 1
# Skip duplicate values for third element
while left < right and nums[right] == nums[right - 1]:
right -= 1
left += 1
right -= 1
elif current_sum < target:
left += 1
else:
right -= 1
return result
Complexity Analysis
- Time Complexity: O(n²), where n is the length of the array. Sorting takes O(n log n), and for each element, we perform a two-pointer search which takes O(n). Overall: O(n log n + n²) = O(n²).
- Space Complexity: O(1) or O(n) depending on the sorting algorithm used. We don’t count the output array in space complexity.
Key Insights
-
Sort First: Sorting enables us to use two pointers efficiently and makes duplicate detection straightforward by checking adjacent elements.
-
Reduce to 2Sum: By fixing one element and finding pairs that sum to its negative, we convert 3Sum into multiple 2Sum problems.
- Skip Duplicates Carefully: We need to skip duplicates at all three positions:
- Fixed element:
if i > 0 and nums[i] == nums[i - 1]: continue - After finding a match: skip duplicates for both left and right pointers
- Fixed element:
-
Target Optimization: Instead of checking if
nums[i] + nums[left] + nums[right] == 0, we check ifnums[left] + nums[right] == -nums[i], which is clearer. - Early Termination: If
nums[i] > 0, we can break early since all remaining elements will be positive (array is sorted), making it impossible to sum to 0.