2Sum, 3Sum, 3Sum Closest, 4Sum

2Sum

Given an array of integers, find two numbers such that they add up to a specific target number.
The function twoSum should return indices of the two numbers such that they add up to the target, where index1 must be less than index2. Please note that your returned answers (both index1 and index2) are not zero-based.
You may assume that each input would have exactly one solution.
Input: numbers={2, 7, 11, 15}, target=9
Output: index1=1, index2=2

Solution

Two Pointer Solution:
First copy numbers[] into old[] and sort old[]. Then we use two pointers to scan for the target sum. At last, we recover the indexes of these two elements in the unsorted array. This takes O(nlogn) time.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public int[] twoSum(int[] numbers, int target) {
int[] result = new int[2];
int[] old = new int[numbers.length];
System.arraycopy(numbers, 0, old, 0, numbers.length);
Arrays.sort(old);
int i=0;
int j=numbers.length-1;
//use i, j to scan the whole array
while(i<j)
{
if(old[i]+old[j]>target)
{j--;
continue;}
else if(old[i]+old[j]<target)
{
i++;
continue;}
else break;
}
int n1=old[i];
int n2=old[j];
int a = -1,b=-1;
//recover indexes of the original array
for (int ii=0; ii<numbers.length;ii++){
if(numbers[ii]==n1 || numbers[ii]==n2){
if(a==-1){a=ii+1;}
else b=ii+1;
}
}
result[0]=a;
result[1]=b;
Arrays.sort(result);
return result;
}

HashTable Solution:

During one pass, we put element and index (as key, value) into the map, meanwhile we search for target-num[i] in our map. If found, get the two indexes. This takes O(n) time.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public int[] twoSum(int[] numbers, int target) {
HashMap<Integer, Integer> map = new HashMap<Integer, Integer>();
int n = numbers.length;
int[] result = new int[2];
for (int i = 0; i < numbers.length; i++)
{
if (map.containsKey(target - numbers[i]))
{
result[0] = map.get(target-numbers[i]) + 1;
result[1] = i + 1;
break;
}
else
{
map.put(numbers[i], i);
}
}
return result;
}

3Sum

Given an array S of n integers, are there elements a, b, c in S such that a + b + c = 0? Find all unique triplets in the array which gives the sum of zero.
Note:
Elements in a triplet (a,b,c) must be in non-descending order. (ie, a ≤ b ≤ c)
The solution set must not contain duplicate triplets.
For example, given array S = {-1 0 1 2 -1 -4},
A solution set is:
(-1, 0, 1)
(-1, -1, 2)

Solution

Brute force method.
Use three loops, for each i,j,k indexes check if 3 sum equals to 0. In order to avoid duplicate sets, we make the following obeservations:

  • The first element must be non-positive
  • Given the first element, the first two element must sum to non-positive
  • Note: the Solution doesn’t handle duplicates, and O(N^3) exceeded limited time.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    public ArrayList<ArrayList<Integer>> threeSum(int[] num) {
    Arrays.sort(num);
    ArrayList<ArrayList<Integer>> res= new ArrayList<ArrayList<Integer>>();
    ArrayList<Integer> each=new ArrayList<Integer>();
    for (int i=0;i<num.length;i++){
    if(num[i]>0) break;
    for (int j=i;j<num.length;j++){
    if(num[i]+num[j]>0) break;
    for(int k=j;k<num.length;k++){
    if(num[i]+num[j]+num[k]==0){
    each.add(num[i]);
    each.add(num[j]);
    each.add(num[k]);
    res.add(each);
    each.clear();
    }
    }
    }
    }
    return res;
    }

    Better Solution:

    Similar to how we used 2 pointers in 2Sum, we now use three pointers to solve 3Sum. For removing duplicate sets, we first sort input.

  • For the first element, we only run checking process for the first of duplicate elements.
  • We use two pointers to check for cases such that num[i]+num[l]+num[r]==0. If found, move both pointers closer (don’t forget to skip duplicates), if not found, move only one pointer.
  • Overall runtime is O(N^2) and we can guarantee no duplicate sets.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    public ArrayList<ArrayList<Integer>> threeSum(int[] num) {
    Arrays.sort(num);
    ArrayList<ArrayList<Integer>> res= new ArrayList<ArrayList<Integer>>();
    int l=0,r=0;
    for (int i=0;i<num.length-2;i++){
    if(num[i]>0) break;
    //skip the duplicated first element
    else if(i>0 && num[i]==num[i-1]) continue;
    l=i+1;
    r=num.length-1;
    //use left and right pointer to scan the rest
    while(l<r){
    if(num[i]+num[l]+num[r]==0){
    ArrayList<Integer> tmp=new ArrayList<Integer>();
    tmp.add(num[i]);
    tmp.add(num[l]);
    tmp.add(num[r]);
    res.add(tmp);
    l++;
    r--;
    //skip duplicates
    while(l<r && num[l]==num[l-1]) l++;
    while(l<r && num[r]==num[r+1]) r--;
    }
    //scan the rest starting with num[i]
    else if(num[i]+num[l]+num[r]<0) l++;
    else r--;
    }
    }
    return res;
    }

    3Sum Closest

    Given an array S of n integers, find three integers in S such that the sum is closest to a given number, target. Return the sum of the three integers. You may assume that each input would have exactly one solution.
    For example, given array S = {-1 2 1 -4}, and target = 1.
    The sum that is closest to the target is 2. (-1 + 2 + 1 = 2).

    Similar to 3Sum, first sort the array and then we use three pointers to scan for the closest sum:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    public int threeSumClosest(int[] num, int target) {
    int cls=Integer.MAX_VALUE,res=Integer.MAX_VALUE;
    int l=0,r=0;
    int[] newnum = new int[num.length];
    System.arraycopy(num, 0, newnum, 0, num.length);
    Arrays.sort(newnum);
    for(int i=0;i<num.length;i++){
    l=i+1; r=num.length-1;
    while(l<r){
    int sum=newnum[i]+newnum[l]+newnum[r];
    int diff=Math.abs(target-sum);
    //if match found, immediately return sum
    if(diff==0) return sum;
    //check if current cls is min, update sum
    else if (diff<cls){
    cls=diff;
    res=sum;
    }
    //move pointers accordingly
    else if(sum-target<=0) l++;
    else {r--;}
    }
    }
    return res;
    }

    4Sum

    Given an array S of n integers, are there elements a, b, c, and d in S such that a + b + c + d = target? Find all unique quadruplets in the array which gives the sum of target.
    Note:
    Elements in a quadruplet (a,b,c,d) must be in non-descending order. (ie, a ≤ b ≤ c ≤ d)
    The solution set must not contain duplicate quadruplets.
    For example, given array S = {1 0 -1 0 -2 2}, and target = 0.
    A solution set is:
    (-1, 0, 0, 1)
    (-2, -1, 1, 2)
    (-2, 0, 0, 2)

    Solution

    • similar to 3sum, use to pointer to find quadruplets faster, O(n^3) time.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    public static List<List<Integer>> fourSum(int[] nums, int target) {
    Arrays.sort(nums);
    List<List<Integer>> res = new ArrayList<List<Integer>>();
    for(int i=0; i<nums.length-3; i++){
    if (i > 0 && nums[i] == nums[i-1]) //remove duplicates
    continue;
    for(int j=i+1; j<nums.length-2; j++){
    if (j>i+1 && nums[j]==nums[j-1]) //remove duplicates
    continue;
    int left = j+1;
    int right = nums.length-1;
    while(left<nums.length && left<right){
    if(nums[left]+ nums[right] == target-nums[i]-nums[j]) {
    ArrayList<Integer> tmp = new ArrayList<Integer>();
    int leftnum = nums[left];
    tmp.add(nums[i]);
    tmp.add(nums[j]);
    tmp.add(nums[left]);
    tmp.add(nums[right]);
    res.add(tmp);
    while(left<nums.length && nums[left] == leftnum){
    left++;
    }
    }
    else if(nums[left]+ nums[right] > target-nums[i]-nums[j]){
    right--;
    }
    else{
    left++;
    }
    }
    }
    }
    return res;
    }