3Sum – Leetcode #15

PROBLEM STATEMENT

This problem is from Leetcode – 3Sum. The problem statement in short is given below:

Given an integer array nums, find all unique triplets [a, b, c] such that a + b + c = 0.

Return a list of all such unique triplets. The solution set must not contain duplicate triplets.

SOLUTION

FIRST APPROACH

Lets try the brute force approach first.

public List<List<Integer>> threeSum(int[] nums) {

		Set<List<Integer>> threeSum = new HashSet<List<Integer>>();	
		List<Integer> sumList = new ArrayList<Integer>();

		int len = nums.length;

		for(int i=0; i<len-2; i++) {

			int first = nums[i];
			for(int j=i+1; j<len-1; j++) {

				int second = nums[j];
				for(int k=j+1; k<len; k++) {

					int third = nums[k];

					if(first + second + third == 0  ) {

						sumList = new ArrayList<Integer>();
						
						sumList = Arrays.asList(first, second, third);

						// sort list before adding
						Collections.sort(sumList);

						threeSum.add(sumList);
						
					} // endIf
				} 
			} 
		} 


		return new ArrayList<List<Integer>>(threeSum);
	}

You can checkout the code from Github here – 3Sum – FirstApproach

PERFORMANCE ANALYSIS

COMPLEXITYO (N3)

SECOND APPROACH

Lets try the HashSet Approach.

public List<List<Integer>> threeSum(int[] nums) {

		Set<List<Integer>> threeSum = new HashSet<List<Integer>>();	
		List<Integer> sumList = new ArrayList<Integer>();
		
		
		Arrays.sort(nums);
		
		int len = nums.length;
			

		for(int i=0; i<len-2; i++) {
			
			// skip duplicates
			if(i>0 && nums[i] == nums[i-1]) 
				continue;

			int first = nums[i];
			Set<Integer> numsSet = new HashSet<>();
			
			for(int j=i+1; j<len; j++) {

				int second = nums[j];
				
				// target val
				int third = (first+second)*-1;
								
				// check if target exists in map
				if(numsSet.contains(third)) {

					sumList = new ArrayList<Integer>();
					
					sumList.add(first);
					sumList.add(second);
					sumList.add(third);

					// sort list before adding
					Collections.sort(sumList);
					threeSum.add(sumList);

				
				}
				numsSet.add(nums[j]);

			} 
		} 

		return new ArrayList<List<Integer>>(threeSum);
	
		
	}

You can checkout the code from Github here – 3Sum – SecondApproach

PERFORMANCE ANALYSIS

RUNTIME437 ms | Beats 18.00 %
MEMORY53.59 MB | Beats 9.07 %
TIME COMPLEXITYO (N2)
SPACE COMPLEXITYO (N)

THIRD APPROACH

example:
        int nums = {1,2,-3}

        Create a pairwise 2-D sum matrix as below. Skip when i == j :


            0    1     2
          _______________ 
     0   |  -    3    -2 
     1   |  3    -    -1
     2   | -2   -1     - 

   
       Create a HashMap with indices as key and sum as val:

       map.add("0,1",  3);
       map.add("0,2", -2);
       map.add("1,0",  3);
       map.add("1,2", -1);
       map.add("2,0", -2);
       map.add("2,1", -1);

       This approach ensures that key is different even if the sum is same.
       
      

We’ll use this 2-D sum matrix to find the complement. See the code sample below:

public List<List<Integer>> threeSum(int[] nums) {

		Set<List<Integer>> threeSum = new HashSet<List<Integer>>();	
		List<Integer> sumList = new ArrayList<Integer>();

		Map<String, Integer> numsSumMap = new HashMap<>();

		int len = nums.length;
		
		// create the 2-D sum matrix
		for(int i=0; i<len; i++) {

			for(int j=0; j<len; j++) {

				if(i!=j) {
					
					String val = i +"," + j;
					numsSumMap.put( val, nums[i] + nums[j]);
				}
			}
		}

		
		// now traverse the input and see if its complement exists in above matrix
		for(int k=0; k<len; k++) {

			int target = nums[k]*-1;
			int third = nums[k];

			if(numsSumMap.containsValue(target)) {

				for(String key: numsSumMap.keySet()) {

					if(numsSumMap.get(key).equals(target)) {

						String val = key;

						String[] valArr = val.split(",");

						int i = Integer.parseInt(valArr[0]);
						int j = Integer.parseInt(valArr[1]);

						int first = nums[i];
						int second = nums[j];

						if(  i != j && j!=k && i!=k ) {
						
							sumList = Arrays.asList(first, second, third);

							// sort list before adding
							Collections.sort(sumList);

							threeSum.add(sumList);
						} // endIf
					}
				}				

			}
		}

		return new ArrayList<List<Integer>>(threeSum);

	}

You can checkout the code from Github here – 3Sum – ThirdApproach

FOURTH APPROACH


Take the above pairwise 2-D sum matrix and flatten it.

            0    1     2
          _______________ 
     0   |  -    3    -2 
     1   |  3    -    -1
     2   | -2   -1     - 

                ||
                \/

     ___              ___              ___
    | 0 | - | 3 | -2 | 1 | 3 | - | -1 | 2 | -2 | -1 | - | 

      |                |                | 
    index            index            index


The idea is to reduce the traverse time from O(N2) to O(N) while searching for the complement. See the code sample below:

public List<List<Integer>> threeSum(int[] nums) {

		Set<List<Integer>> threeSum = new HashSet<List<Integer>>();	
		List<Integer> sumList = new ArrayList<Integer>();

		int len = nums.length;
		Integer[][] numsSumMat = new Integer[len][len];

		for(int i=0; i<len; i++) {

			for(int j=0; j<len; j++) {

				if(i != j) {
					numsSumMat[i][j] =  nums[i] + nums[j] ;
				}
				else {
					numsSumMat[i][j] =  Integer.MAX_VALUE;
				}


			}
		}

		// flatten 2d array
		Integer[] numSumFlatten = new Integer[(len*len) + len];
		int idx = 0;
		for(int i=0; i<len; i++) {

			numSumFlatten[idx++] = i;
			for(int j=0; j<len; j++) {

				numSumFlatten[idx++] = numsSumMat[i][j] ;
			}
		}


		// now traverse and find the complement
		for(int index=0; index<len; index++) {

			int target = nums[index]*-1;
			int third = nums[index];

			int row = 0;
			int col = 0;
			int count = 1;
			
			for(int k=0; k<numSumFlatten.length; k++) {

				if(numSumFlatten[k] == target  && 
						numSumFlatten[k] != Integer.MAX_VALUE  &&
						col != 0 && index != row && index != (col-1) ) {

					int first = nums[row];
					int second = nums[col-1];

					sumList = sumList = Arrays.asList(first, second, third);

					// sort list before adding
					Collections.sort(sumList);

					threeSum.add(sumList);

				}

				// col goes from 0 to len
				if(count == len+1) {

					col = 0;
					row = row+1;
					count = 1;				

				}
				else {
					col++;
					count = count+1;
				}			

			}// endfor
		}

		return new ArrayList<List<Integer>>(threeSum);
	}

You can checkout the code from Github here – 3Sum – FourthApproach

FIFTH APPROACH

Finally, lets try the 2-pointer approach. See the code sample below:

public List<List<Integer>> threeSum(int[] nums) {

		List<List<Integer>> threeSum = new ArrayList<List<Integer>>();	

		Arrays.sort(nums);

		int len = nums.length;

		for(int i=0; i<len-2; i++) {

			// skip duplicates
			if(i>0 && nums[i] == nums[i-1]) {
				continue;
			}

			int j = i+1;
			int k = nums.length-1;

			while(j < k) {

				int sum = nums[i] + nums[j] + nums[k];

				if(sum == 0) {

					threeSum.add(Arrays.asList(nums[i] , nums[j] , nums[k]));

					// skip duplicates
					while (j < k && nums[k] == nums[k - 1]) k--;
					while (j < k && nums[j] == nums[j + 1]) j++; 

					j++;
					k--;

				} //endif
				else if(sum < 0) {

					j++;
				}
				else if(sum > 0) {

					k--;
				}

			} // endwhile

		}// end outer for loop

		return threeSum;

	}		

You can checkout the code from Github here – 3Sum – FifthApproach

PERFORMANCE ANALYSIS

RUNTIME30 ms | Beats 70.19 %
MEMORY51.55 MB | Beats 57.01 %
TIME COMPLEXITYO (N2)
SPACE COMPLEXITYO (K)

Leave a Reply

Your email address will not be published. Required fields are marked *

Related Posts

Begin typing your search term above and press enter to search. Press ESC to cancel.

Back To Top