2 minute read

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.

 

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.

Intuition

My initial thoughts on how to solve this problem revolve around utilizing the two-pointer approach to efficiently explore triplets that sum to zero. Sorting the array allows us to efficiently navigate through different combinations.

Approach

My approach involves sorting the array to simplify the process of finding triplets that sum to zero. I iterate through the array and use two pointers (left and right) to explore possible triplets. I handle duplicates to avoid redundant solutions. Whenever the sum of the current triplet is zero, I add it to the result. I continue iterating and adjusting the pointers based on the comparison with zero.

Complexity

  • Time complexity: O(n^2), where n is the length of the input array. The sorting step takes O(n log n) time, and the two-pointer approach involves a nested loop with linear time complexity.

  • Space complexity: O(1) as we use a constant amount of space to store variables (res, num, l, r, curr_sum) without relying on additional data structures that scale with the input size.

Code

class Solution:
    def threeSum(self, nums: List[int]) -> List[List[int]]:
        nums.sort()
        res = []
        
        for i in range(len(nums) - 2):
            # Skip duplicates to avoid redundant solutions
            if i > 0 and nums[i - 1] == nums[i]:
                continue
            
            num, l, r = nums[i], i + 1, len(nums) - 1
            
            while l < r:
                curr_sum = num + nums[l] + nums[r]
                
                if curr_sum == 0:
                    res.append([num, nums[l], nums[r]])
                    l += 1
                    r -= 1

                    # Skip duplicates to avoid redundant solutions
                    while l < r and nums[l] == nums[l - 1]:
                        l += 1
                        
                elif curr_sum > 0:
                    r -= 1
                else:
                    l += 1
                
        return res

Editorial Solution

class Solution:
    def threeSum(self, nums: List[int]) -> List[List[int]]:
        res = []
        nums.sort()
        for i in range(len(nums)):
            if nums[i] > 0:
                break
            if i == 0 or nums[i - 1] != nums[i]:
                self.twoSumII(nums, i, res)
        return res

    def twoSumII(self, nums: List[int], i: int, res: List[List[int]]):
        lo, hi = i + 1, len(nums) - 1
        while (lo < hi):
            sum = nums[i] + nums[lo] + nums[hi]
            if sum < 0:
                lo += 1
            elif sum > 0:
                hi -= 1
            else:
                res.append([nums[i], nums[lo], nums[hi]])
                lo += 1
                hi -= 1
                while lo < hi and nums[lo] == nums[lo - 1]:
                    lo += 1

No sort Approach

class Solution:
    def threeSum(self, nums: List[int]) -> List[List[int]]:
        res, dups = set(), set()
        seen = {}
        for i, val1 in enumerate(nums):
            if val1 not in dups:
                dups.add(val1)
                for j, val2 in enumerate(nums[i+1:]):
                    complement = -val1 - val2
                    if complement in seen and seen[complement] == i:
                        res.add(tuple(sorted((val1, val2, complement))))
                    seen[val2] = i
        return res
  • Time complexity: O(n^2)
  • Space complexity: O(n)