Three Sum Problem¶
Problem Statement¶
Given an array of integers nums, find all unique triplets in the array that sum up to zero. 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[4] + nums[2] = (-1) + (-1) + 2 = 0
- nums[0] + nums[2] + nums[1] = (-1) + 1 + 0 = 0
Example 2:¶
Input: nums = [0, 0, 0, 0]
Output: [[0, 0, 0]]
Explanation: The only possible triplet sums to zero
Example 3:¶
Input: nums = [1, 2, -2, -1]
Output: []
Explanation: No triplet sums to zero
Approaches¶
1. Brute Force Approach (Not Recommended)¶
Check every possible triplet combination (three nested loops).
public class ThreeSum {
public List<List<Integer>> findThreeSum_BruteForce(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); // For consistent ordering
result.add(triplet);
}
}
}
}
return new ArrayList<>(result);
}
}
Time Complexity: O(n³) Space Complexity: O(n) - for storing the result
2. Two Pointers Approach (Optimal)¶
Sort the array first, then use one fixed element and two pointers for the remaining sum.
public class ThreeSum {
public List<List<Integer>> findThreeSum_TwoPointers(int[] nums) {
List<List<Integer>> result = new ArrayList<>();
Arrays.sort(nums); // Sort array first
for (int i = 0; i < nums.length - 2; i++) {
// Skip duplicates for i
if (i > 0 && nums[i] == nums[i - 1]) continue;
int left = i + 1;
int 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]));
// Skip duplicates for left
while (left < right && nums[left] == nums[left + 1]) left++;
// Skip duplicates for right
while (left < right && nums[right] == nums[right - 1]) right--;
left++;
right--;
} else if (sum < 0) {
left++;
} else {
right--;
}
}
}
return result;
}
}
Time Complexity: O(n²) Space Complexity: O(1) - excluding the space for output
3. Hash Set Approach¶
Use a hash set to find the third number after fixing the first two numbers.
public class ThreeSum {
public List<List<Integer>> findThreeSum_HashSet(int[] nums) {
Set<List<Integer>> result = new HashSet<>();
Arrays.sort(nums); // Sort for duplicate handling
for (int i = 0; i < nums.length - 2; i++) {
Set<Integer> seen = new HashSet<>();
for (int j = i + 1; j < nums.length; j++) {
int complement = -(nums[i] + nums[j]);
if (seen.contains(complement)) {
result.add(Arrays.asList(nums[i], complement, nums[j]));
}
seen.add(nums[j]);
}
}
return new ArrayList<>(result);
}
}
Time Complexity: O(n²) Space Complexity: O(n) - for the hash set
Complete Solution with Tests¶
import java.util.*;
public class ThreeSum {
// All approaches implemented above
public static void main(String[] args) {
// Test cases
int[][] testArrays = {
{-1, 0, 1, 2, -1, -4},
{0, 0, 0, 0},
{1, 2, -2, -1}
};
ThreeSum solution = new ThreeSum();
for (int i = 0; i < testArrays.length; i++) {
System.out.println("Test Case " + (i + 1) + ":");
System.out.println("Array: " + Arrays.toString(testArrays[i]));
// Test all approaches
System.out.println("Two Pointers: " +
solution.findThreeSum_TwoPointers(testArrays[i]));
System.out.println("Hash Set: " +
solution.findThreeSum_HashSet(testArrays[i]));
System.out.println();
}
}
}
Optimization Techniques¶
-
Early Termination:
// If first number is greater than 0, no solution possible if (nums[i] > 0) break; -
Skip Duplicates:
// Skip duplicate values for first number if (i > 0 && nums[i] == nums[i - 1]) continue; -
Bounds Check:
// Check if enough elements remain if (i > nums.length - 3) break;
Common Pitfalls and Tips¶
- Duplicate Triplets: Make sure to handle duplicate triplets properly.
- Array Sorting: Consider whether sorting the array first helps.
- Empty Array: Handle cases where array length is less than 3.
- Memory Usage: Be careful with creating new lists/arrays.
- Integer Overflow: Consider potential overflow when summing numbers.
Interview Tips¶
- Start by sorting the array and explain why it helps.
- Explain how to handle duplicates.
- Discuss the space-time tradeoff between approaches.
- Consider writing helper methods for cleaner code.
- Mention possible optimizations.
Follow-up Questions¶
- What if we need to find four numbers that sum to zero?
- Can we solve it without sorting the array?
- How would you handle very large input arrays?
- What if we need to find triplets that sum to a target value other than zero?
- How would you parallelize this for very large arrays?
Real-world Applications¶
- Financial analysis for identifying balanced transactions
- 3D graphics for point cloud processing
- Chemical compound balancing
- Network packet analysis
- Data clustering algorithms
Performance Comparison¶
| Approach | Time Complexity | Space Complexity | Best For |
|---|---|---|---|
| Brute Force | O(n³) | O(1) | Small arrays |
| Two Pointers | O(n²) | O(1) | Most cases |
| Hash Set | O(n²) | O(n) | When sorting is expensive |
Testing Edge Cases¶
// Test empty array
int[] empty = {};
assert solution.findThreeSum_TwoPointers(empty).isEmpty();
// Test array with less than 3 elements
int[] tooSmall = {1, 2};
assert solution.findThreeSum_TwoPointers(tooSmall).isEmpty();
// Test array with all zeros
int[] allZeros = {0, 0, 0, 0, 0};
assert solution.findThreeSum_TwoPointers(allZeros).size() == 1;
// Test array with no solution
int[] noSolution = {1, 2, 3, 4, 5};
assert solution.findThreeSum_TwoPointers(noSolution).isEmpty();
// Test array with multiple solutions
int[] multiple = {-1, -1, -1, 0, 0, 0, 1, 1, 1};
// Should contain multiple unique triplets