3Sum
Problem Description
Given an integer array nums, return all the triplets [nums[i], nums[j], nums[k]] such that i, j, and k are distinct indices and nums[i] + nums[j] + nums[k] == 0.
The solution set must not contain duplicate triplets.
Examples
Example 1:Input: nums = [-1,0,1,2,-1,-4] Output: [[-1,-1,2],[-1,0,1]]
Input: nums = [0,1,1] Output: []
Input: nums = [0,0,0] Output: [[0,0,0]]
Constraints
3 ≤ nums.length ≤ 3000-10⁵ ≤ nums[i] ≤ 10⁵
Fix One Number and Reduce to Two Sum
Three nested loops is O(n³) and generates duplicates. Fix one number by iterating i, then reduce the remaining problem to Two Sum on a sorted subarray, which two pointers solve in O(n). That gets you to O(n²) total.
Sorting first also gives you a free early exit: if nums[i] > 0, no triplet summing to zero can start here (all remaining numbers are non-negative). And sorted duplicates are adjacent, so skipping them becomes a one-line check.
Solution 1: Brute Force with a Set
Three nested loops checking all triplets. Use a set to deduplicate.
import java.util.*;
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
Set<List<Integer>> result = new HashSet<>();
for (int i = 0; i < nums.length - 2; i++) {
for (int j = i + 1; j < nums.length - 1; j++) {
for (int k = j + 1; k < nums.length; k++) {
if (nums[i] + nums[j] + nums[k] == 0) {
List<Integer> triplet = Arrays.asList(nums[i], nums[j], nums[k]);
Collections.sort(triplet);
result.add(triplet);
}
}
}
}
return new ArrayList<>(result);
}
}class Solution:
def threeSum(self, nums: list[int]) -> list[list[int]]:
result = set()
for i in range(len(nums) - 2):
for j in range(i + 1, len(nums) - 1):
for k in range(j + 1, len(nums)):
if nums[i] + nums[j] + nums[k] == 0:
result.add(tuple(sorted([nums[i], nums[j], nums[k]])))
return [list(t) for t in result]#include <vector>
#include <set>
#include <algorithm>
class Solution {
public:
std::vector<std::vector<int>> threeSum(std::vector<int>& nums) {
std::set<std::vector<int>> result;
int n = nums.size();
for (int i = 0; i < n - 2; i++)
for (int j = i + 1; j < n - 1; j++)
for (int k = j + 1; k < n; k++)
if (nums[i] + nums[j] + nums[k] == 0) {
std::vector<int> t = {nums[i], nums[j], nums[k]};
std::sort(t.begin(), t.end());
result.insert(t);
}
return std::vector<std::vector<int>>(result.begin(), result.end());
}
};Complexity Analysis:
- Time Complexity: O(n³). Three nested loops plus sorting each triplet.
- Space Complexity: O(n). The result set in the worst case.
Where it breaks: at n = 3000, this is 27 billion operations. Well beyond what passes in any time limit.
Solution 2: Sort + Two Pointers
Sort the array. For each i, use two pointers on nums[i+1..n-1] to find pairs summing to -nums[i]. Skip duplicate values of i and of left/right after finding a triplet to avoid duplicates in the output.
import java.util.*;
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
Arrays.sort(nums);
List<List<Integer>> result = new ArrayList<>();
for (int i = 0; i < nums.length - 2; i++) {
if (nums[i] > 0) break; // all remaining sums are positive
if (i > 0 && nums[i] == nums[i - 1]) continue; // skip duplicate first elements
int left = i + 1, right = nums.length - 1;
while (left < right) {
int sum = nums[i] + nums[left] + nums[right];
if (sum == 0) {
result.add(Arrays.asList(nums[i], nums[left], nums[right]));
while (left < right && nums[left] == nums[left + 1]) left++;
while (left < right && nums[right] == nums[right - 1]) right--;
left++;
right--;
} else if (sum < 0) {
left++;
} else {
right--;
}
}
}
return result;
}
}class Solution:
def threeSum(self, nums: list[int]) -> list[list[int]]:
nums.sort()
result = []
for i in range(len(nums) - 2):
if nums[i] > 0:
break # all remaining sums are positive
if i > 0 and nums[i] == nums[i - 1]:
continue # skip duplicate first elements
left, right = i + 1, len(nums) - 1
while left < right:
s = nums[i] + nums[left] + nums[right]
if s == 0:
result.append([nums[i], nums[left], nums[right]])
while left < right and nums[left] == nums[left + 1]: left += 1
while left < right and nums[right] == nums[right - 1]: right -= 1
left += 1
right -= 1
elif s < 0:
left += 1
else:
right -= 1
return result#include <vector>
#include <algorithm>
class Solution {
public:
std::vector<std::vector<int>> threeSum(std::vector<int>& nums) {
std::sort(nums.begin(), nums.end());
std::vector<std::vector<int>> result;
for (int i = 0; i < (int)nums.size() - 2; i++) {
if (nums[i] > 0) break;
if (i > 0 && nums[i] == nums[i - 1]) continue;
int left = i + 1, right = nums.size() - 1;
while (left < right) {
int sum = nums[i] + nums[left] + nums[right];
if (sum == 0) {
result.push_back({nums[i], nums[left], nums[right]});
while (left < right && nums[left] == nums[left + 1]) left++;
while (left < right && nums[right] == nums[right - 1]) right--;
left++; right--;
} else if (sum < 0) {
left++;
} else {
right--;
}
}
}
return result;
}
};Complexity Analysis:
- Time Complexity: O(n²). Sorting is O(n log n); the outer loop runs O(n) times and each inner two-pointer scan is O(n).
- Space Complexity: O(1) extra, not counting the output.
Where it breaks: if you forget the duplicate-skip logic for left and right after finding a match, you’ll include duplicate triplets in the output. The array [-2, 0, 0, 2, 2] is a good test case: the pair [0, 2] appears twice.
Common Mistakes
- Forgetting
i > 0before comparingnums[i] == nums[i-1]. Without this guard, you accessnums[-1]on the first iteration. - Skipping duplicates before adding the triplet. The skip loop for
leftandrightadvances to the last duplicate, then theleft++; right--at the end moves past it. If you do the skip before theresult.add, you’d miss the valid triplet. - Not breaking when
nums[i] > 0. Continuing past positive first elements wastes time and can produce wrong results if you mismanage the skip logic.
Frequently Asked Questions
Can you solve 3Sum in better than O(n²)? No known algorithm solves 3Sum in better than O(n²) for the general case. It’s conjectured to be a hard lower bound.
What if the array has fewer than 3 elements?
The constraint guarantees n >= 3, so you don’t need to handle this.
How does this extend to 4Sum? Add another outer loop fixing a second element, reducing it to a Two Sum inside. That gives O(n³). The same skip-duplicate logic applies at each level.