0015. 3Sum¶
Table of Contents¶
- Problem
- Problem Breakdown
- Approach 1: Brute Force
- Approach 2: Sort + Two Pointers
- Complexity Comparison
- Edge Cases
- Common Mistakes
- Related Problems
- Visual Animation
Problem¶
| Leetcode | 15. 3Sum |
| Difficulty | |
| Tags | Array, Two Pointers, Sorting |
Description¶
Given an integer array
nums, return all the triplets[nums[i], nums[j], nums[k]]such thati != j,i != k, andj != k, andnums[i] + nums[j] + nums[k] == 0.Notice that 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]]
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 <= 3000-10^5 <= nums[i] <= 10^5
Problem Breakdown¶
1. What is being asked?¶
Find all unique triplets in the array that sum to zero. Return the values (not indices), and there must be no duplicate triplets in the result.
2. What is the input?¶
| Parameter | Type | Description |
|---|---|---|
nums | int[] | Array of integers |
Important observations about the input: - The array is unsorted (not sorted) - Duplicates may exist (Example 1: two -1 values) - The array contains at least 3 elements - Negative numbers are possible
3. What is the output?¶
- A list of triplets
[[a, b, c], ...] - Each triplet satisfies
a + b + c = 0 - No duplicate triplets (e.g.,
[-1,0,1]should appear only once) - Order of triplets and order within a triplet do not matter
- Return an empty list if no triplets exist
4. Constraints analysis¶
| Constraint | Significance |
|---|---|
nums.length <= 3000 | O(n^2) works (~9,000,000 operations), O(n^3) is borderline (~27 billion) |
-10^5 <= nums[i] <= 10^5 | int32 is sufficient, sum of three fits easily |
| No duplicate triplets | Must handle deduplication carefully |
5. Step-by-step example analysis¶
Example 1: nums = [-1, 0, 1, 2, -1, -4]¶
After sorting: [-4, -1, -1, 0, 1, 2]
Fix i=0 (nums[i]=-4): target = 4
left=1 (-1), right=5 (2): -1 + 2 = 1 < 4 → left++
left=2 (-1), right=5 (2): -1 + 2 = 1 < 4 → left++
left=3 (0), right=5 (2): 0 + 2 = 2 < 4 → left++
left=4 (1), right=5 (2): 1 + 2 = 3 < 4 → left++
left=5, left >= right → stop
Fix i=1 (nums[i]=-1): target = 1
left=2 (-1), right=5 (2): -1 + 2 = 1 == 1 ✅ → triplet: [-1, -1, 2]
Skip duplicates → left=3, right=4
left=3 (0), right=4 (1): 0 + 1 = 1 == 1 ✅ → triplet: [-1, 0, 1]
left=4, right=3, left >= right → stop
Fix i=2 (nums[i]=-1): SKIP — duplicate of i=1
Fix i=3 (nums[i]=0): target = 0
left=4 (1), right=5 (2): 1 + 2 = 3 > 0 → right--
left=4, right=4, left >= right → stop
Result: [[-1, -1, 2], [-1, 0, 1]]
Example 3: nums = [0, 0, 0]¶
After sorting: [0, 0, 0]
Fix i=0 (nums[i]=0): target = 0
left=1 (0), right=2 (0): 0 + 0 = 0 == 0 ✅ → triplet: [0, 0, 0]
left=2, right=1 → stop
Result: [[0, 0, 0]]
6. Key Observations¶
- Sorting enables deduplication — Identical values are adjacent, so skipping duplicates is easy.
- Reduces to Two Sum — For each fixed element
nums[i], find two elements summing to-nums[i]. - Two Pointers on sorted array — After fixing
i, useleftandrightpointers converging inward. - Early termination — If
nums[i] > 0after sorting, no valid triplet is possible (all remaining values are positive).
7. Pattern identification¶
| Pattern | Why it fits | Example |
|---|---|---|
| Sort + Two Pointers | O(n^2), natural deduplication | 3Sum (this problem) |
| Hash Set | O(n^2), uses set for dedup | Works but harder to implement cleanly |
| Brute Force | Always works, but slow | All problems |
Chosen pattern: Sort + Two Pointers Reason: Sorting makes duplicate skipping trivial, and Two Pointers gives O(n) for the inner loop, yielding O(n^2) total.
Approach 1: Brute Force¶
Thought process¶
The simplest idea: check all possible triplets using three nested loops. Sort first so that duplicates can be detected by comparing with previous values. Use a set to avoid duplicate triplets.
Algorithm (step-by-step)¶
- Sort the array
- Three nested loops:
i,j = i+1,k = j+1 - If
nums[i] + nums[j] + nums[k] == 0→ add to result (if not already seen) - Use a set of tuples for deduplication
Pseudocode¶
function threeSum(nums):
sort(nums)
result = []
seen = set()
for i = 0 to n-3:
for j = i+1 to n-2:
for k = j+1 to n-1:
if nums[i] + nums[j] + nums[k] == 0:
triplet = (nums[i], nums[j], nums[k])
if triplet not in seen:
seen.add(triplet)
result.append([nums[i], nums[j], nums[k]])
return result
Complexity¶
| Complexity | Explanation | |
|---|---|---|
| Time | O(n^3) | Three nested loops checking all triplets |
| Space | O(n) | Set for storing seen triplets |
Implementation¶
Go¶
func threeSumBruteForce(nums []int) [][]int {
sort.Ints(nums)
n := len(nums)
result := [][]int{}
seen := map[[3]int]bool{}
for i := 0; i < n-2; i++ {
for j := i + 1; j < n-1; j++ {
for k := j + 1; k < n; k++ {
if nums[i]+nums[j]+nums[k] == 0 {
triplet := [3]int{nums[i], nums[j], nums[k]}
if !seen[triplet] {
seen[triplet] = true
result = append(result, []int{nums[i], nums[j], nums[k]})
}
}
}
}
}
return result
}
Java¶
public List<List<Integer>> threeSumBruteForce(int[] nums) {
Arrays.sort(nums);
int n = nums.length;
Set<List<Integer>> resultSet = new LinkedHashSet<>();
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) {
resultSet.add(Arrays.asList(nums[i], nums[j], nums[k]));
}
}
}
}
return new ArrayList<>(resultSet);
}
Python¶
def threeSumBruteForce(self, nums: list[int]) -> list[list[int]]:
nums.sort()
n = len(nums)
result = []
seen = set()
for i in range(n - 2):
for j in range(i + 1, n - 1):
for k in range(j + 1, n):
if nums[i] + nums[j] + nums[k] == 0:
triplet = (nums[i], nums[j], nums[k])
if triplet not in seen:
seen.add(triplet)
result.append([nums[i], nums[j], nums[k]])
return result
Dry Run¶
Input: nums = [-1, 0, 1, 2, -1, -4]
After sorting: [-4, -1, -1, 0, 1, 2]
i=0 (-4):
j=1 (-1): k=2: -4-1-1=-6 ❌ k=3: -4-1+0=-5 ❌ k=4: -4-1+1=-4 ❌ k=5: -4-1+2=-3 ❌
j=2 (-1): k=3: -4-1+0=-5 ❌ k=4: -4-1+1=-4 ❌ k=5: -4-1+2=-3 ❌
j=3 (0): k=4: -4+0+1=-3 ❌ k=5: -4+0+2=-2 ❌
j=4 (1): k=5: -4+1+2=-1 ❌
i=1 (-1):
j=2 (-1): k=3: -1-1+0=-2 ❌ k=4: -1-1+1=-1 ❌ k=5: -1-1+2=0 ✅ → [-1,-1,2]
j=3 (0): k=4: -1+0+1=0 ✅ → [-1,0,1] k=5: -1+0+2=1 ❌
j=4 (1): k=5: -1+1+2=2 ❌
i=2 (-1):
j=3 (0): k=4: -1+0+1=0 ✅ → [-1,0,1] DUPLICATE (already seen)
...
Total comparisons: 20 (for n=6)
Result: [[-1,-1,2], [-1,0,1]]
Approach 2: Sort + Two Pointers¶
The problem with Brute Force¶
O(n^3) is too slow for n = 3000 (27 billion operations). We need to reduce the inner search from O(n^2) to O(n).
Optimization idea¶
- Sort the array
- Fix one element (
nums[i]), reducing the problem to Two Sum on a sorted subarray- Two Pointers (
left,right) converging inward to find pairs summing to-nums[i]- Skip duplicates at all three positions to avoid duplicate triplets
Algorithm (step-by-step)¶
- Sort the array
- For each
ifrom0ton-3: - If
i > 0andnums[i] == nums[i-1]→ skip (duplicate first element) - If
nums[i] > 0→ break (early termination) - Set
left = i + 1,right = n - 1,target = -nums[i] - While
left < right:sum = nums[left] + nums[right]- If
sum == target→ add triplet, skip duplicates, move both pointers - If
sum < target→left++ - If
sum > target→right--
Pseudocode¶
function threeSum(nums):
sort(nums)
result = []
for i = 0 to n-3:
if i > 0 and nums[i] == nums[i-1]: continue
if nums[i] > 0: break
left = i + 1, right = n - 1
target = -nums[i]
while left < right:
sum = nums[left] + nums[right]
if sum == target:
result.append([nums[i], nums[left], nums[right]])
while left < right and nums[left] == nums[left+1]: left++
while left < right and nums[right] == nums[right-1]: right--
left++; right--
elif sum < target:
left++
else:
right--
return result
Complexity¶
| Complexity | Explanation | |
|---|---|---|
| Time | O(n^2) | O(n log n) sort + O(n) outer loop * O(n) two-pointer = O(n^2) |
| Space | O(1) | In-place sorting, no extra data structures (output not counted) |
Implementation¶
Go¶
func threeSum(nums []int) [][]int {
sort.Ints(nums)
n := len(nums)
result := [][]int{}
for i := 0; i < n-2; i++ {
if i > 0 && nums[i] == nums[i-1] { continue }
if nums[i] > 0 { break }
left, right := i+1, n-1
target := -nums[i]
for left < right {
sum := nums[left] + nums[right]
if sum == target {
result = append(result, []int{nums[i], nums[left], nums[right]})
for left < right && nums[left] == nums[left+1] { left++ }
for left < right && nums[right] == nums[right-1] { right-- }
left++
right--
} else if sum < target {
left++
} else {
right--
}
}
}
return result
}
Java¶
public List<List<Integer>> threeSum(int[] nums) {
Arrays.sort(nums);
int n = nums.length;
List<List<Integer>> result = new ArrayList<>();
for (int i = 0; i < n - 2; i++) {
if (i > 0 && nums[i] == nums[i - 1]) continue;
if (nums[i] > 0) break;
int left = i + 1, right = n - 1;
int target = -nums[i];
while (left < right) {
int sum = nums[left] + nums[right];
if (sum == target) {
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 < target) {
left++;
} else {
right--;
}
}
}
return result;
}
Python¶
def threeSum(self, nums: list[int]) -> list[list[int]]:
nums.sort()
n = len(nums)
result = []
for i in range(n - 2):
if i > 0 and nums[i] == nums[i - 1]:
continue
if nums[i] > 0:
break
left, right = i + 1, n - 1
target = -nums[i]
while left < right:
total = nums[left] + nums[right]
if total == target:
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 total < target:
left += 1
else:
right -= 1
return result
Dry Run¶
Input: nums = [-1, 0, 1, 2, -1, -4]
After sorting: [-4, -1, -1, 0, 1, 2]
i=0, nums[i]=-4, target=4:
left=1(-1), right=5(2): -1+2=1 < 4 → left++
left=2(-1), right=5(2): -1+2=1 < 4 → left++
left=3(0), right=5(2): 0+2=2 < 4 → left++
left=4(1), right=5(2): 1+2=3 < 4 → left++
left=5 >= right=5 → DONE
i=1, nums[i]=-1, target=1:
left=2(-1), right=5(2): -1+2=1 == 1 ✅ → [-1,-1,2]
Skip dups → left=3, right=4
left=3(0), right=4(1): 0+1=1 == 1 ✅ → [-1,0,1]
left=4, right=3 → DONE
i=2, nums[i]=-1: SKIP (duplicate, nums[2]==nums[1])
i=3, nums[i]=0, target=0:
left=4(1), right=5(2): 1+2=3 > 0 → right--
left=4 >= right=4 → DONE
Result: [[-1,-1,2], [-1,0,1]]
Total operations: ~12 (vs 20 for Brute Force on this small input)
Complexity Comparison¶
| # | Approach | Time | Space | Pros | Cons |
|---|---|---|---|---|---|
| 1 | Brute Force | O(n^3) | O(n) | Simple logic | Too slow, TLE on large inputs |
| 2 | Sort + Two Pointers | O(n^2) | O(1) | Optimal, clean dedup | Requires sorting (modifies input) |
Which solution to choose?¶
- In an interview: Approach 2 (Sort + Two Pointers) — optimal and demonstrates mastery of two patterns
- In production: Approach 2 — best time complexity, minimal memory
- On Leetcode: Approach 2 — easily passes all test cases
- For learning: Both — Brute Force shows the problem clearly, Two Pointers shows the optimization
Edge Cases¶
| # | Case | Input | Expected Output | Reason |
|---|---|---|---|---|
| 1 | LeetCode Example | [-1,0,1,2,-1,-4] | [[-1,-1,2],[-1,0,1]] | Standard case with duplicates |
| 2 | All zeros | [0,0,0] | [[0,0,0]] | Only valid triplet is three zeros |
| 3 | No valid triplets | [0,1,1] | [] | No three numbers sum to zero |
| 4 | All positive | [1,2,3,4,5] | [] | Positive numbers cannot sum to zero |
| 5 | All negative | [-5,-4,-3,-2,-1] | [] | Negative numbers cannot sum to zero |
| 6 | Many duplicates | [0,0,0,0,0] | [[0,0,0]] | Deduplication must produce only one triplet |
| 7 | Minimum length | [-1,0,1] | [[-1,0,1]] | Exactly 3 elements |
| 8 | Two elements | [-1,1] | [] | Less than 3 elements — no triplet possible |
Common Mistakes¶
Mistake 1: Not skipping duplicates for the first element¶
# ❌ WRONG — duplicate triplets in result
for i in range(n - 2):
left, right = i + 1, n - 1
# ...finds [-1,0,1] twice when two -1's exist
# ✅ CORRECT — skip duplicate first elements
for i in range(n - 2):
if i > 0 and nums[i] == nums[i - 1]:
continue
# ...
Reason: If nums[1] == nums[2] == -1, both iterations produce the same set of triplets.
Mistake 2: Not skipping duplicates for left/right after finding a triplet¶
# ❌ WRONG — finds the same triplet multiple times
if total == target:
result.append([nums[i], nums[left], nums[right]])
left += 1
right -= 1
# ✅ CORRECT — skip all duplicate values
if total == target:
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
Reason: After finding [-1, 0, 1], if there are multiple 0s or 1s, the same triplet would be added again.
Mistake 3: Forgetting early termination¶
# ❌ SLOWER — continues even when nums[i] > 0
for i in range(n - 2):
if i > 0 and nums[i] == nums[i - 1]: continue
# ...searches pointlessly when all remaining values are positive
# ✅ FASTER — break early
for i in range(n - 2):
if i > 0 and nums[i] == nums[i - 1]: continue
if nums[i] > 0: break # No triplet possible
# ...
Reason: After sorting, if nums[i] > 0, then nums[left] > 0 and nums[right] > 0, so their sum can never be zero.
Mistake 4: Forgetting to sort the array¶
# ❌ WRONG — Two Pointers requires a sorted array
def threeSum(self, nums):
for i in range(len(nums)):
left, right = i + 1, len(nums) - 1
# Two Pointers on unsorted array — incorrect results!
# ✅ CORRECT — sort first
def threeSum(self, nums):
nums.sort() # Essential for Two Pointers!
# ...
Related Problems¶
| # | Problem | Difficulty | Similarity |
|---|---|---|---|
| 1 | 1. Two Sum | Foundation — find two numbers summing to target | |
| 2 | 16. 3Sum Closest | Same pattern, find closest sum instead of exact | |
| 3 | 18. 4Sum | Extension to 4 elements, same Sort + Two Pointers | |
| 4 | 167. Two Sum II - Sorted | Two Pointers on sorted array (inner loop of 3Sum) | |
| 5 | 259. 3Sum Smaller | Count triplets with sum < target |
Visual Animation¶
Interactive animation: animation.html
In the animation: - Brute Force tab — three nested loops check all triplets - Two Pointers tab — sort + fix one element + two pointers converging - Visualizes pointer movement, duplicate skipping, and triplet collection