Three Sum (Triplets that Sum to Zero)

Coding Challenge

medium
O(n²)
O(1)
arraystwo-pointerssortinghash-table

Language Selection

Choose your preferred programming language

Showing: Python

Three Sum (Triplets that Sum to Zero)

Problem Statement

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.

Constraints:

  • 3 <= nums.length <= 3000
  • -10^5 <= nums[i] <= 10^5

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.
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.

Approach 1: Two Pointers (Optimal)

Algorithm:

  1. Sort the array
  2. Fix the first element and use two pointers for the remaining two elements
  3. Skip duplicates to avoid duplicate triplets
  4. Move pointers based on the current sum

Time Complexity: O(n²)
Space Complexity: O(1)

Python:

def threeSum(nums):
    """
    Find all unique triplets that sum to zero using two pointers
    Time: O(n²)
    Space: O(1)
    """
    nums.sort()
    result = []
    
    for i in range(len(nums) - 2):
        # Skip duplicates for the first element
        if i > 0 and nums[i] == nums[i - 1]:
            continue
        
        left, right = i + 1, len(nums) - 1
        
        while left < right:
            current_sum = nums[i] + nums[left] + nums[right]
            
            if current_sum == 0:
                result.append([nums[i], nums[left], nums[right]])
                
                # Skip duplicates for the second element
                while left < right and nums[left] == nums[left + 1]:
                    left += 1
                
                # Skip duplicates for the third element
                while left < right and nums[right] == nums[right - 1]:
                    right -= 1
                
                left += 1
                right -= 1
            elif current_sum < 0:
                left += 1
            else:
                right -= 1
    
    return result

Java:

class Solution {
    /**
     * Find all unique triplets that sum to zero using two pointers
     * Time: O(n²)
     * Space: O(1)
     */
    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++) {
            // Skip duplicates for the first element
            if (i > 0 && nums[i] == nums[i - 1]) {
                continue;
            }
            
            int left = i + 1;
            int right = nums.length - 1;
            
            while (left < right) {
                int currentSum = nums[i] + nums[left] + nums[right];
                
                if (currentSum == 0) {
                    result.add(Arrays.asList(nums[i], nums[left], nums[right]));
                    
                    // Skip duplicates for the second element
                    while (left < right && nums[left] == nums[left + 1]) {
                        left++;
                    }
                    
                    // Skip duplicates for the third element
                    while (left < right && nums[right] == nums[right - 1]) {
                        right--;
                    }
                    
                    left++;
                    right--;
                } else if (currentSum < 0) {
                    left++;
                } else {
                    right--;
                }
            }
        }
        
        return result;
    }
}

Go:

// threeSum - Find all unique triplets that sum to zero using two pointers
// Time: O(n²)
// Space: O(1)
func threeSum(nums []int) [][]int {
    sort.Ints(nums)
    var result [][]int
    
    for i := 0; i < len(nums)-2; i++ {
        // Skip duplicates for the first element
        if i > 0 && nums[i] == nums[i-1] {
            continue
        }
        
        left, right := i+1, len(nums)-1
        
        for left < right {
            currentSum := nums[i] + nums[left] + nums[right]
            
            if currentSum == 0 {
                result = append(result, []int{nums[i], nums[left], nums[right]})
                
                // Skip duplicates for the second element
                for left < right && nums[left] == nums[left+1] {
                    left++
                }
                
                // Skip duplicates for the third element
                for left < right && nums[right] == nums[right-1] {
                    right--
                }
                
                left++
                right--
            } else if currentSum < 0 {
                left++
            } else {
                right--
            }
        }
    }
    
    return result
}

JavaScript:

/**
 * Find all unique triplets that sum to zero using two pointers
 * Time: O(n²)
 * Space: O(1)
 */
function threeSum(nums) {
    nums.sort((a, b) => a - b);
    const result = [];
    
    for (let i = 0; i < nums.length - 2; i++) {
        // Skip duplicates for the first element
        if (i > 0 && nums[i] === nums[i - 1]) {
            continue;
        }
        
        let left = i + 1;
        let right = nums.length - 1;
        
        while (left < right) {
            const currentSum = nums[i] + nums[left] + nums[right];
            
            if (currentSum === 0) {
                result.push([nums[i], nums[left], nums[right]]);
                
                // Skip duplicates for the second element
                while (left < right && nums[left] === nums[left + 1]) {
                    left++;
                }
                
                // Skip duplicates for the third element
                while (left < right && nums[right] === nums[right - 1]) {
                    right--;
                }
                
                left++;
                right--;
            } else if (currentSum < 0) {
                left++;
            } else {
                right--;
            }
        }
    }
    
    return result;
}

C#:

public class Solution {
    /// <summary>
    /// Find all unique triplets that sum to zero using two pointers
    /// Time: O(n²)
    /// Space: O(1)
    /// </summary>
    public IList<IList<int>> ThreeSum(int[] nums) {
        Array.Sort(nums);
        IList<IList<int>> result = new List<IList<int>>();
        
        for (int i = 0; i < nums.Length - 2; i++) {
            // Skip duplicates for the first element
            if (i > 0 && nums[i] == nums[i - 1]) {
                continue;
            }
            
            int left = i + 1;
            int right = nums.Length - 1;
            
            while (left < right) {
                int currentSum = nums[i] + nums[left] + nums[right];
                
                if (currentSum == 0) {
                    result.Add(new List<int> { nums[i], nums[left], nums[right] });
                    
                    // Skip duplicates for the second element
                    while (left < right && nums[left] == nums[left + 1]) {
                        left++;
                    }
                    
                    // Skip duplicates for the third element
                    while (left < right && nums[right] == nums[right - 1]) {
                        right--;
                    }
                    
                    left++;
                    right--;
                } else if (currentSum < 0) {
                    left++;
                } else {
                    right--;
                }
            }
        }
        
        return result;
    }
}

Approach 2: Hash Set

Algorithm:

  1. Sort the array
  2. Fix the first element and use a hash set to find the third element
  3. Skip duplicates to avoid duplicate triplets

Time Complexity: O(n²)
Space Complexity: O(n)

Python:

def threeSum(nums):
    """
    Find all unique triplets that sum to zero using hash set
    Time: O(n²)
    Space: O(n)
    """
    nums.sort()
    result = []
    
    for i in range(len(nums) - 2):
        # Skip duplicates for the first element
        if i > 0 and nums[i] == nums[i - 1]:
            continue
        
        seen = set()
        target = -nums[i]
        
        for j in range(i + 1, len(nums)):
            complement = target - nums[j]
            
            if complement in seen:
                result.append([nums[i], complement, nums[j]])
                
                # Skip duplicates for the second element
                while j + 1 < len(nums) and nums[j] == nums[j + 1]:
                    j += 1
            
            seen.add(nums[j])
    
    return result

Java:

class Solution {
    /**
     * Find all unique triplets that sum to zero using hash set
     * Time: O(n²)
     * Space: O(n)
     */
    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++) {
            // Skip duplicates for the first element
            if (i > 0 && nums[i] == nums[i - 1]) {
                continue;
            }
            
            Set<Integer> seen = new HashSet<>();
            int target = -nums[i];
            
            for (int j = i + 1; j < nums.length; j++) {
                int complement = target - nums[j];
                
                if (seen.contains(complement)) {
                    result.add(Arrays.asList(nums[i], complement, nums[j]));
                    
                    // Skip duplicates for the second element
                    while (j + 1 < nums.length && nums[j] == nums[j + 1]) {
                        j++;
                    }
                }
                
                seen.add(nums[j]);
            }
        }
        
        return result;
    }
}

Go:

// threeSum - Find all unique triplets that sum to zero using hash set
// Time: O(n²)
// Space: O(n)
func threeSum(nums []int) [][]int {
    sort.Ints(nums)
    var result [][]int
    
    for i := 0; i < len(nums)-2; i++ {
        // Skip duplicates for the first element
        if i > 0 && nums[i] == nums[i-1] {
            continue
        }
        
        seen := make(map[int]bool)
        target := -nums[i]
        
        for j := i + 1; j < len(nums); j++ {
            complement := target - nums[j]
            
            if seen[complement] {
                result = append(result, []int{nums[i], complement, nums[j]})
                
                // Skip duplicates for the second element
                for j+1 < len(nums) && nums[j] == nums[j+1] {
                    j++
                }
            }
            
            seen[nums[j]] = true
        }
    }
    
    return result
}

JavaScript:

/**
 * Find all unique triplets that sum to zero using hash set
 * Time: O(n²)
 * Space: O(n)
 */
function threeSum(nums) {
    nums.sort((a, b) => a - b);
    const result = [];
    
    for (let i = 0; i < nums.length - 2; i++) {
        // Skip duplicates for the first element
        if (i > 0 && nums[i] === nums[i - 1]) {
            continue;
        }
        
        const seen = new Set();
        const target = -nums[i];
        
        for (let j = i + 1; j < nums.length; j++) {
            const complement = target - nums[j];
            
            if (seen.has(complement)) {
                result.push([nums[i], complement, nums[j]]);
                
                // Skip duplicates for the second element
                while (j + 1 < nums.length && nums[j] === nums[j + 1]) {
                    j++;
                }
            }
            
            seen.add(nums[j]);
        }
    }
    
    return result;
}

C#:

public class Solution {
    /// <summary>
    /// Find all unique triplets that sum to zero using hash set
    /// Time: O(n²)
    /// Space: O(n)
    /// </summary>
    public IList<IList<int>> ThreeSum(int[] nums) {
        Array.Sort(nums);
        IList<IList<int>> result = new List<IList<int>>();
        
        for (int i = 0; i < nums.Length - 2; i++) {
            // Skip duplicates for the first element
            if (i > 0 && nums[i] == nums[i - 1]) {
                continue;
            }
            
            HashSet<int> seen = new HashSet<int>();
            int target = -nums[i];
            
            for (int j = i + 1; j < nums.Length; j++) {
                int complement = target - nums[j];
                
                if (seen.Contains(complement)) {
                    result.Add(new List<int> { nums[i], complement, nums[j] });
                    
                    // Skip duplicates for the second element
                    while (j + 1 < nums.Length && nums[j] == nums[j + 1]) {
                        j++;
                    }
                }
                
                seen.Add(nums[j]);
            }
        }
        
        return result;
    }
}

Approach 3: Brute Force

Algorithm:

  1. Generate all possible triplets
  2. Check if they sum to zero
  3. Use a set to avoid duplicates

Time Complexity: O(n³)
Space Complexity: O(1)

Python:

def threeSum(nums):
    """
    Find all unique triplets that sum to zero using brute force
    Time: O(n³)
    Space: O(1)
    """
    result = []
    nums.sort()
    
    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:
                    triplet = [nums[i], nums[j], nums[k]]
                    if triplet not in result:
                        result.append(triplet)
    
    return result

Java:

class Solution {
    /**
     * Find all unique triplets that sum to zero using brute force
     * Time: O(n³)
     * Space: O(1)
     */
    public List<List<Integer>> threeSum(int[] nums) {
        List<List<Integer>> result = new ArrayList<>();
        Arrays.sort(nums);
        
        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]);
                        if (!result.contains(triplet)) {
                            result.add(triplet);
                        }
                    }
                }
            }
        }
        
        return result;
    }
}

Go:

// threeSum - Find all unique triplets that sum to zero using brute force
// Time: O(n³)
// Space: O(1)
func threeSum(nums []int) [][]int {
    sort.Ints(nums)
    var result [][]int
    
    for i := 0; i < len(nums)-2; i++ {
        for j := i + 1; j < len(nums)-1; j++ {
            for k := j + 1; k < len(nums); k++ {
                if nums[i]+nums[j]+nums[k] == 0 {
                    triplet := []int{nums[i], nums[j], nums[k]}
                    
                    // Check if triplet already exists
                    exists := false
                    for _, existing := range result {
                        if slices.Equal(triplet, existing) {
                            exists = true
                            break
                        }
                    }
                    
                    if !exists {
                        result = append(result, triplet)
                    }
                }
            }
        }
    }
    
    return result
}

JavaScript:

/**
 * Find all unique triplets that sum to zero using brute force
 * Time: O(n³)
 * Space: O(1)
 */
function threeSum(nums) {
    const result = [];
    nums.sort((a, b) => a - b);
    
    for (let i = 0; i < nums.length - 2; i++) {
        for (let j = i + 1; j < nums.length - 1; j++) {
            for (let k = j + 1; k < nums.length; k++) {
                if (nums[i] + nums[j] + nums[k] === 0) {
                    const triplet = [nums[i], nums[j], nums[k]];
                    
                    // Check if triplet already exists
                    const exists = result.some(existing => 
                        existing.every((val, idx) => val === triplet[idx])
                    );
                    
                    if (!exists) {
                        result.push(triplet);
                    }
                }
            }
        }
    }
    
    return result;
}

C#:

public class Solution {
    /// <summary>
    /// Find all unique triplets that sum to zero using brute force
    /// Time: O(n³)
    /// Space: O(1)
    /// </summary>
    public IList<IList<int>> ThreeSum(int[] nums) {
        IList<IList<int>> result = new List<IList<int>>();
        Array.Sort(nums);
        
        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<int> triplet = new List<int> { nums[i], nums[j], nums[k] };
                        
                        // Check if triplet already exists
                        bool exists = result.Any(existing => 
                            existing.SequenceEqual(triplet)
                        );
                        
                        if (!exists) {
                            result.Add(triplet);
                        }
                    }
                }
            }
        }
        
        return result;
    }
}

Key Insights

  1. Sorting is crucial: It allows us to use two pointers and skip duplicates efficiently.
  2. Two Pointers Technique: After fixing one element, we can use two pointers to find the other two elements in O(n) time.
  3. Duplicate Handling: We must skip duplicates at each level to avoid duplicate triplets in the result.
  4. Early Termination: If the first element is positive, we can break early since all remaining elements will also be positive.

Edge Cases

  • All zeros: [0,0,0] → result is [[0,0,0]]
  • No solution: [1,2,3] → result is []
  • All negative: [-1,-2,-3] → result is []
  • All positive: [1,2,3] → result is []
  • Duplicates: [-1,-1,0,1,1] → result is [[-1,0,1]]

Test Cases

# Test case 1: Mixed positive and negative
assert threeSum([-1,0,1,2,-1,-4]) == [[-1,-1,2],[-1,0,1]]

# Test case 2: No solution
assert threeSum([0,1,1]) == []

# Test case 3: All zeros
assert threeSum([0,0,0]) == [[0,0,0]]

# Test case 4: Single solution
assert threeSum([-1,0,1]) == [[-1,0,1]]

# Test case 5: Multiple duplicates
assert threeSum([-2,0,1,1,2]) == [[-2,0,2],[-2,1,1]]

Follow-up Questions

  1. Four Sum: Find all unique quadruplets that sum to a target value.
  2. Three Sum Closest: Find three integers whose sum is closest to a target.
  3. Three Sum Smaller: Count the number of triplets with sum smaller than target.
  4. K Sum: Generalize to find k elements that sum to target.

Common Mistakes

  1. Not handling duplicates: Forgetting to skip duplicate elements at each level.
  2. Incorrect pointer movement: Not moving both pointers when a valid triplet is found.
  3. Off-by-one errors: Incorrect loop bounds or pointer initialization.
  4. Not sorting: Trying to solve without sorting first.

Trade-offs

  • Two Pointers vs Hash Set: Two pointers is more space-efficient, hash set is easier to understand
  • Brute Force: Simple but inefficient for large inputs
  • Space vs Time: Hash set uses O(n) space but same time complexity
  • Sorting: Required for efficient duplicate handling