Grokking the Coding Interview: Patterns for Coding Questions
Vote

0% completed

​

Solution: Triplet Sum to Zero

Problem Statement

Solution

Step-by-Step Algorithm

Algorithm Walkthrough

Code

Complexity Analysis:

Time Complexity

Space Complexity

Problem Statement

Given an array of unsorted numbers, find all unique triplets in it that add up to zero.

Example 1:

Input: [-3, 0, 1, 2, -1, 1, -2]
Output: [[-3, 1, 2], [-2, 0, 2], [-2, 1, 1], [-1, 0, 1]]
Explanation: There are four unique triplets whose sum is equal to zero. smallest sum.'

Example 2:

Input: [-5, 2, -1, -2, 3]
Output: [[-5, 2, 3], [-2, -1, 3]]
Explanation: There are two unique triplets whose sum is equal to zero.

Constraints:

  • 3 <= arr.length <= 3000
  • -10<sup>5</sup> <= arr[i] <= 10<sup>5</sup>

Solution

This problem follows the Two Pointers pattern and shares similarities with Pair with Target Sum. A couple of differences are that the input array is not sorted and instead of a pair we need to find triplets with a target sum of zero.

To follow a similar approach, first, we will sort the array and then iterate through it taking one number at a time. Let’s say during our iteration we are at number ‘X’, so we need to find ‘Y’ and ‘Z’ such that X + Y + Z == 0. At this stage, our problem translates into finding a pair whose sum is equal to “-X” (as from the above equation Y + Z==−X).

Another difference from Pair with Target Sum is that we need to find all the unique triplets. To handle this, we have to skip any duplicate number. Since we will be sorting the array, so all the duplicate numbers will be next to each other and are easier to skip.

Step-by-Step Algorithm

  1. Sort the Array:

    • Sort the input array in non-decreasing order. This helps in easily skipping duplicate elements and applying the two-pointer technique.
  2. Iterate through the Array:

    • Use a for loop to iterate through the array, stopping at the third-to-last element.
    • For each element, check if it's the same as the previous one to avoid duplicates.
    • If it's the same, skip to the next iteration.
  3. Fix the Current Element and Find Pairs:

    • Fix the current element and use two pointers to find pairs whose sum is the negative of the fixed element (targetSum = -arr[i]).
    • The left pointer starts from the next element (i + 1) and the right pointer starts from the end of the array.
  4. Find Pairs with Two Pointers:

    • Calculate the sum of the left pointer and the right pointer (currentSum = arr[left] + arr[right]).
    • If the currentSum equals targetSum, add the triplet to the list ([-targetSum, arr[left], arr[right]]) and move both pointers to the next unique elements.
    • If currentSum is less than targetSum, move the left pointer to the right to increase the sum.
    • If currentSum is greater than targetSum, move the right pointer to the left to decrease the sum.
  5. Skip Duplicates:

    • Ensure that the left and right pointers skip duplicate elements to avoid counting the same triplet multiple times.
  6. Return the Result:

    • After processing all elements, return the list of unique triplets.

Algorithm Walkthrough

Let's walk through the algorithm with the input [-5, 2, -1, -2, 3].

  1. Sort the Array:

    • Input: [-5, 2, -1, -2, 3]
    • Sorted: [-5, -2, -1, 2, 3]
  2. Fix -5 (index 0), left pointer at -2 (index 1), and right pointer at 3 (index 4):

    • Target Sum: -(-5) = 5
    • Sum = -2 + 3 = 1 (less than targetSum), move left to -1 (index 2)
    • Sum = -1 + 3 = 2 (less than targetSum), move left to 2 (index 3)
    • Sum = 2 + 3 = 5, found triplet [-5, 2, 3]
    • Move left to 4 and right to 3 (end of this iteration)
  3. Fix -2 (index 1), left pointer at -1 (index 2), and right pointer at 3 (index 4):

    • Target Sum: -(-2) = 2
    • Sum = -1 + 3 = 2, found triplet [-2, -1, 3]
    • Move left to 2 and right to 3
  4. Fix -1 (index 2), left pointer at 2 (index 3), and right pointer at 3 (index 4):

    • Target Sum: -(-1) = 1
    • Sum = 2 + 3 = 5 (greater than targetSum), move right to 2 (end of this iteration)
  5. End of loop since all elements are processed.

Output: [[ -5, 2, 3], [ -2, -1, 3]]

Let's visualize the example 2 via below diagram.

Image

Code

Here is what our algorithm will look like:

Python3
Python3

. . . .

Complexity Analysis:

Time Complexity

  • Sorting the array: The first step in the algorithm is to sort the input array, which takes O(N \log N), where N is the number of elements in the input array.

  • Outer loop: The main loop runs for each element in the array, excluding the last two, as those will be processed during the pair search. The loop runs approximately N - 2 times, which is O(N).

  • Inner loop (pair search): For each element processed by the outer loop, the algorithm uses the two-pointer technique to search for pairs that sum to the target value. The two-pointer search for each element takes O(N) time since the pointers traverse the entire array in a linear fashion.

  • Overall time complexity: The total time complexity is the sum of the time spent sorting the array and the time spent in the two-pointer search for each element. This gives us a total time complexity of O(N \log N + N^2). Since N^2 dominates N \log N for large values of N, the overall time complexity is O(N^2).

Space Complexity

  • Sorting the array: Sorting the array requires additional space, and this adds O(N) space complexity.

  • Triplets storage: The space used to store the resulting triplets is O(K), where K is the number of triplets found. In the worst case, this could be proportional to O(N^2), especially if there are many valid triplets.

  • Additional variables: The algorithm uses constant extra space for variables like left, right, and currentSum, so this adds O(1) to the space complexity.

Overall space complexity: O(N^2) for storing the output triplets and O(1) for extra variables, resulting in O(N^2) overall.

A

Anonymous

· 2 years ago

func findTripletSumToZero(arr: [Int]) -> [[Int]] {

    let set = Set(arr)

    var triplets = Set<Set<Int>>()

    var i = 0

    var j = 1

    while (i < arr.count && j < arr.count) {

        if (!set.contains(-1 * (arr[i] + arr[j]))) {

            if (i == j) {

                j += 2

            } else {

                j += 1

            }

        } else {

            let triplet = Set([arr[i], arr[j], -1 * (arr[i] + arr[j])])

            if (!triplets.contains(triplet)) {

                triplets.insert(triplet)

            }

            if (i == j) {

                i += 2

            } else {

                i += 1

            }

        }

    }

    return triplets.map { set in

        set.map { elem in

            elem

        }

    }

}

A

Anonymous

· 2 years ago

You only need to ensure two of the three numbers are not repeating. Because if 2 of the 3 are unique then the triplet is inherently unique. The second while loop for adjusting the right pointer is not necessary and makes the solution a more confusing.

Show 1 reply
A

Anonymous

· 3 years ago

class Solution:   def searchTriplets(self, arr):     triplets = []     # TODO: Write your code here     arr.sort()     n=len(arr)     for i in range(0,n-2):#go upto the third last element only       el=arr[i]       if i>0 and arr[i]==arr[i-1]:         continue       j=i+1       k=n-1       while(j<k):         #TODO: logic for skipping second element already chosen         el2=arr[j]         el3=arr[k]         if arr[j]+arr[k]==(0-el):           triplets.append([el,el2,el3])           j+=1           k-=1             while(j<k and arr[j]==el2):             j+=1           while(j<k and arr[k]==el3):             k-=1         elif arr[j]+arr[k]<(0-el):           j+=1           while(j<k and arr[j]==el2):             j+=1         else:           k-=1           while(j<k and arr[k
Show 1 reply
A

Anonymous

· 3 years ago

The solution for this doesn't work, getting runtime errors for the Javascript version. Even if I copy the exact solution and paste it.

Show 2 replies
A

Anonymous

· 3 years ago

It seems like the only accepted solutions also sort the result array which doesn't seem to be part of the problem statement. If it is not a hard requirement, instead of checking for duplicates within searchPair, we can also append tuples to the list and then use a set to only leave the unique tuples before converting the tuples into lists.

A

Anonymous

· 3 years ago

/** Sort the array , iterate over the array by having left and right pointers and find the sum. */ public Set<List<Integer>> searchTriplets2(int[] array) { Arrays.sort(array); Set<List<Integer>> triplets = new HashSet<>(); // Iterate only length -2 since you cant make triplets with only 2 elements for (int i = 0; i < array.length - 2; i++) { // Left is 1 ahead of i when it starts int right = array.length - 1; // Right always starts from the end int left = i+1; while (left < right) { int currentSum = array[i] + array[left] + array[right]; if (currentSum == 0) { triplets.add(Arrays.asList(array[i], array[left], array[right])); left++; right--;
Show 1 reply
A

Anonymous

· 3 years ago

Isn't sorting done in place? If so, shouldn't the space complexity because be constant if we are ignore the output array?

Show 1 reply
A

Anonymous

· 3 years ago

I've read and watched a few videos on this problem and I really think it would be helpful if there was a good visual representation of what is going on. Especially when the "correct" solution here is very complicated and verbose.

A

Anonymous

· 4 years ago

If you find a triplet on the first call to seach_pair you will get a list index error because right already equals the end of the list so right +1 is out of bounds.

Show 1 reply
A

Anonymous

· 4 years ago

The code itself is pretty complicated, but this video gives a better visualization of how it works. Check it out: https://m.youtube.com/watch?v=PXpthXV1mmw

Show 4 replies

Reading Progress

0%


Vote for new content

On This Page

Problem Statement

Solution

Step-by-Step Algorithm

Algorithm Walkthrough

Code

Complexity Analysis:

Time Complexity

Space Complexity