Back to blog
Nov 26, 2024
3 min read

LeetCode 15: 3Sum

Leetcode 15: 3Sum solution in Python

Problem Description

LeetCode Problem 15

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
  • -105 <= nums[i] <= 105

Difficulty: Medium

Tags: array, two pointers, sorting

Rating: 91.42%

Solution Approach

Let’s break down the solution step by step, understanding each optimization and technique used.

Key Insights

  1. Sorting First:

    • By sorting the array first, we can:
      • Easily skip duplicates
      • Use the two-pointer technique
      • Enable early termination optimizations
  2. Early Termination:

    • If the smallest three numbers sum to > 0, we can stop
    • If the largest three numbers sum to < 0, we can skip the current number
    • If all positive or all negative numbers, return empty result
  3. Two-Pointer Technique:

    • After fixing one number, use two pointers to find the other two numbers
    • Move pointers based on sum comparison with target

Implementation

class Solution:
    def threeSum(self, nums: List[int]) -> List[List[int]]:
        # Edge case handling
        if len(nums) < 3: 
            return []
            
        # Sort array for two-pointer technique
        nums.sort()
        n = len(nums)
        result = []
        
        # Early termination if all positive or all negative
        if nums[0] > 0 or nums[-1] < 0: 
            return []
            
        # Fix first number and use two pointers for other two
        for i in range(n-2):
            # Skip duplicates for first number
            if i > 0 and nums[i] == nums[i-1]: 
                continue
                
            # Early termination optimizations
            if nums[i] + nums[i+1] + nums[i+2] > 0: 
                break  # smallest possible sum > 0
            if nums[i] + nums[n-2] + nums[n-1] < 0: 
                continue  # largest possible sum < 0
                
            left = i + 1
            right = n - 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 duplicates for both pointers
                    left += 1
                    right -= 1
                    while left < right and nums[left] == nums[left-1]: 
                        left += 1
                    while left < right and nums[right] == nums[right+1]: 
                        right -= 1
                        
                elif current_sum < target:
                    left += 1
                else:
                    right -= 1
                    
        return result

Complexity Analysis

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

  • Sorting: O(n log n)
  • Main loop: O(n)
    • Two-pointer technique for each iteration: O(n)
  • Overall: O(n²) as the nested loop dominates

Space Complexity: O(1)O(1) or O(n)O(n)

  • O(1) extra space excluding the output array
  • O(n) if we count the space used to store the result
  • The sorting algorithm might use additional space depending on the implementation

Optimization Techniques

  1. Early Termination

    if nums[0] > 0 or nums[-1] < 0: 
        return []
    

    This simple check avoids processing arrays that can’t possibly have a solution.

  2. Skip Duplicates

    if i > 0 and nums[i] == nums[i-1]: 
        continue
    

    Skipping duplicates early prevents generating duplicate triplets.

  3. Range Check

    if nums[i] + nums[i+1] + nums[i+2] > 0: break
    if nums[i] + nums[n-2] + nums[n-1] < 0: continue
    

    These checks quickly eliminate ranges that can’t contain valid triplets.