3 sum problem algorithm

Given an array of integers and a target we have to check if there exists a triplet such that their combined sum is equal to the given target. Popularly known as 3 sum problem algorithm.

Example

Input:
arr = [1, 2, 3, 5, 6, 11, 15, 16, 17, 18];
sum = 20;

Output:
true
[1, 16, 3]

3 sum problem algorithm

This is the second question from the series

Naïve brute force solution

3 nested loops can be used to consider every triplets in the given array and determine if desired sum is found.


Naïve recursive solution

Using recursion to solve this.

  • For each item we either consider the current item or ignore it and then recur for the remaining items.
  • If triplet with given target is found return true, otherwise return false.
const threeSum = (arr, n, sum, count) => {
    //if there exits a triplet with given sum
    if(sum === 0 && count === 3){
      return true;
    }
  
    //if there is no triplet with desired sum
    if(count === 3 || n === 0 || sum < 0){
      return false;
    }
  
    //keep recur by
    //considering current element or
    //ignoring current element
    return threeSum(arr, n - 1, sum - arr[n - 1], count + 1) || threeSum(arr, n - 1, sum, count)
}
Input:
const arr = [1, 2, 3, 5, 6, 11, 15, 16, 17, 18];
const sum = 20;
console.log(threeSum(arr, arr.length, sum, 0));

Output:
true

Time complexity: (2 ^ N) because for each function call we are calling the same function twice.
Space complexity: (2 ^ N) considering the call stack, O(1) other wise.


Efficient approach using hashing to solve 3 sum problem algorithm.

In this approach we assume that the triplet can be broken down as a pair plus one extra element. Thus we can use the 2 sum algorithm's logic to solve it.

Conceptually this is how it works.

  • We store each element from the array in the map along with its index.
  • Then we consider all the pair in the array and check if the map has the remaining sum or not.
  • If the index of the remaining sum is not overlapping with the pair indexes then triplets exists thus return true, false otherwise.
const threeSumWithHashing = (arr, sum) => {
  const map = new Map();
  
  // Add element along with its index in the hashmap
  for(let i = 0; i < arr.length; i++){
    map.set(arr[i], i);
  }
  
  // check each pair
  // we ignore last element because we need a triplet (pair + 1 element).
  for(let i = 0; i < arr.length - 1; i++){
    
    for(let j = i + 1; j < arr.length; j++){
      
       //pending sum
       let val = sum - (arr[i] + arr[j]);
      
       // there is a triplet, if we pending sum is available in the map
       if(map.has(val)){
          // if it is unique, return true
	  if (map.get(val) !== i && map.get(val) !== j) {
	     return true;
	  }
        }
      }
    }
    
    // return false, if there is no triplet
    return false;
}
Input:
const arr = [1, 2, 3, 5, 6, 11, 15, 16, 17, 18];
const sum = 20;
console.log(threeSumWithHashing(arr, sum));

Output:
true

Time complexity: O(n + n ^ 2) = O(n ^ 2).
Space complexity: O(n).


Printing all the unique triplets that are possible in 3 sum problem algorithm

To print all the unique triplets we use the same logic as we have used to check if triplet exists or not with little improvements.

First, sort the array in ascending order so that we don't repeat the combination and then for each element in the array we check if triplets exists by adding the current element and pair from the subarray.

const printAllTriplets = (arr, sum) => {
  // increasing order
  arr = arr.sort((a, b) => a - b);
  
  // explore all triplets
  for(let i = 0; i <= arr.length - 3; i++){
    
    // calc pending sum
    let k = sum - arr[i];
    
    // with two pointers, explore all the pairs of subarray for triplets 
    let low = i + 1;
    let high = arr.length - 1;
    
    while (low < high){

	// if total < pending sum, increment lower index 
	if (arr[low] + arr[high] < k) {
	       low++;
	}

	// if total > pending sum, decrement higher index 
	else if (arr[low] + arr[high] > k) {
		high--;
	}

	// otherwise triplet is found
	else {
                //print
          	console.log(arr[i], arr[low], arr[high]);

		// change the indexes
		low++;
		high--;
	}
     }
  }
}
Input:
const arr = [1, 2, 3, 5, 6, 11, 15, 16, 17, 18];
const sum = 20;
printAllTriplets(arr, sum);

Output:
1 16 3
1 17 2

Time complexity: O(n ^ 2).
Space complexity: O(1).