Skip to content

Leetcode 15 - 3Sum

Difficulty: medium

Table of contents

Open Table of contents

Problem

Given an integer array nums, the task is to return all the triplets [nums[i], nums[j], nums[k]] such that i != j, i != k, j != k, and nums[i] + nums[j] + nums[k] == 0. It is important to note 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:
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:

Explanation/Approach

The problem is to find all unique triplets in the array that add up to zero. This problem can be efficiently solved using a combination of sorting and two-pointer technique.

Solution: Two Pointers with Sorting

class Solution:
    def threeSum(self, nums: List[int]) -> List[List[int]]:
        # Resultant list for storing triplets
        res = []

        # Sort the array to simplify finding triplets and avoiding duplicates
        nums = sorted(nums)

        # Iterate over the array
        for i, num1 in enumerate(nums):
            # If the current number is greater than 0, further triplets are impossible
            if num1 > 0:
                break

            # Skip duplicate elements
            if i > 0 and num1 == nums[i-1]:
                continue

            # Initialize two pointers
            l, r = i + 1, len(nums) - 1

            # Iterate with the two pointers
            while r > l:
                three_sum = num1 + nums[l] + nums[r]

                # Move pointers based on the sum
                if three_sum > 0:
                    r -= 1
                elif three_sum < 0:
                    l += 1
                else:
                    # Add the triplet to the result
                    res.append([num1, nums[l], nums[r]])
                    l += 1

                    # Skip duplicates for the left pointer
                    while nums[l] == nums[l-1] and l < r:
                        l += 1

        return res

Time and Memory Complexity

The time complexity is O(n^2), primarily due to the two-pointer traversal for each element. The sorting operation contributes O(n log n), which is overshadowed by the quadratic complexity. The space complexity is O(1) or O(n) depending on the implementation of the sorting algorithm.

Explanation

This solution begins by sorting the array. For each element (skipping duplicates), it looks for a pair in the rest of the array that adds up to the negative of the element. The two pointers (l and r) help in finding these pairs. When a triplet is found, it is added to the result, and the left pointer is moved while skipping over duplicates.

Visualization

You’re correct. Let’s adjust the visualization for Iteration 2 to reflect this step:


Visualization

With the sorted array [-4, -1, -1, 0, 1, 2] from nums = [-1,0,1,2,-1,-4], the algorithm’s process is as follows:

Iteration 1 (fix -4):
    Array: [-4, -1, -1, 0, 1, 2]
            ↑        ↑        ↑
    l = 1, r = 5. The sum is always less than 0, with the smallest sum being -3 (-4 + -1 + 2), so no valid triplet is found.

Iteration 2 (fix first -1):
    Array: [-4, -1, -1, 0, 1, 2]
                 ↑   ↑        ↑
    l = 2, r = 5. Initially, the sum is 0 (-1 + -1 + 2), finding a valid triplet [-1, -1, 2].
    After finding this triplet, we move the left pointer once to avoid duplicates:
    l = 3, r = 5. Now, the sum is 0 (-1 + 0 + 1), finding another valid triplet [-1, 0, 1].

Iteration 3 (skip second -1, duplicate):
    Skipping the second -1 to avoid repeating the same triplet.

Iteration 4 (fix 0):
    Array: [-4, -1, -1, 0, 1, 2]
                        ↑  ↑  ↑
    l = 4, r = 5. The sum is 3 (0 + 1 + 2), which is too large. Adjusting pointers does not find a valid triplet here.

Response:
    [[-1, -1, 2], [-1, 0, 1]]

In each iteration, the algorithm fixes one element (like -4, -1, 0) and uses two pointers (l and r) to find a pair in the rest of the array that adds up to the negative of the fixed element. After finding a valid triplet, the left pointer is moved to avoid duplicates and to find other possible combinations. Skipping duplicates is crucial to avoid repeating the same triplet and to enhance efficiency.