18. 4Sum

2019/10/14 Leetcode

18. 4Sum

Tags: ‘Array’, ‘Hash Table’, ‘Two Pointers’

Given an array nums of n integers and an integer target, are there elements a, b, c, and d in nums such that a + b + c + d = target? Find all unique quadruplets in the array which gives the sum of target.

Note:

The solution set must not contain duplicate quadruplets.

Example:

Given array nums = [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

方法1, 通用k-sum方法 左右夹逼 O(n^3) O(1)

public List<List<Integer>> fourSum(int[] nums, int target) {
    List<List<Integer>> result = new LinkedList<>();
    Arrays.sort(nums);
    if (nums == null || nums.length < 4) return result;
    
    for (int i = 0; i < nums.length - 3; i++) {
        if (i > 0 && nums[i] == nums[i - 1]) continue; // skip duplicates
        for (int j = i + 1; j < nums.length - 2; j++) {
            if (j > i + 1 && nums[j] == nums[j - 1]) continue; // skip duplicates
            
            // inner loop
            int lo = j + 1, hi = nums.length - 1;
            while (lo < hi) {
                int sum = nums[i] + nums[j] + nums[lo] + nums[hi];
                if (sum == target) {
                    List<Integer> temp = new LinkedList<>(Arrays.asList(nums[i], nums[j], nums[lo], nums[hi]));
                    result.add(temp);
                    while (lo < hi && nums[lo] == nums[lo+1]) lo++;
                    while (lo < hi && nums[hi] == nums[hi-1]) hi--;
                    lo++;
                    hi--;

                }else if (sum < target) {
                    while (lo < hi && nums[lo] == nums[lo+1]) lo++;
                    lo++;
                } else {
                    while (lo < hi && nums[hi] == nums[hi-1]) hi--;
                    hi--;
                }
            }
        }
    }
    return result;
}

方法2, 因为返回的是数值而不是index,所以可以用hashmap做缓存 O(n^3) O(n)

public List<List<Integer>> fourSum(int[] nums, int target) {
        List<List<Integer>> result = new LinkedList<>();
        if (nums == null || nums.length < 4) return result;
        Arrays.sort(nums);
        
        Map<Integer, List<int[]>> cache = new HashMap<>();
        for (int i = 0; i < nums.length; i++) {
            for (int j = i + 1; j < nums.length; j++) {
                List<int[]> value = cache.get(nums[i] + nums[j]);
                if (value == null) {
                    value = new ArrayList<>();
                    cache.put(nums[i] + nums[j], value);
                }
                value.add(new int[]{i, j});
            }
        }
        
        Set<String> used = new HashSet<>();
        for (int i = 0; i < nums.length - 3; i++) {
            if (i > 0 && nums[i] == nums[i-1]) continue; // avoid duplicate
            for (int j = i+1; j < nums.length - 2; j++) {
                if (j > i + 1 && nums[j] == nums[j-1]) continue; // avoid duplicate
                List<int[]> list = cache.get(target - nums[i] - nums[j]);
                if (list == null) {
                    continue;
                } else {
                    // Found a match
                    for (int[] pair: list) {
                        if (j >= pair[0]) continue; // overlap
                        
                        Integer[] temp = new Integer[]{nums[i], nums[j], nums[pair[0]], nums[pair[1]]}; // Integer is required
                        String key = Arrays.toString(temp); 
                        if (!used.contains(key)) {
                            result.add(Arrays.asList(temp));    
                            used.add(key);
                        }
                    } 
                    
                }
            }
        }
        return result;
    }

第一次尝试

class Solution {
    public List<List<Integer>> fourSum(int[] nums, int target) {
        List<List<Integer>> list = new LinkedList<>();
        Arrays.sort(nums);
        for(int i = 0;i< nums.length - 3;i++){
            if(i>0 && nums[i] == nums[i-1]) continue;
            for(int j = i+1;j< nums.length - 2;j++){
                if(j > i+1 && nums[j] == nums[j-1]) continue;
                int twoSumTarget = target - nums[i] - nums[j];
                //The following 3 lines of code to calculate the min and max of twoSum
                int minTwoSum = nums[j+1] + nums[j+2];
                int maxTwoSum = nums[nums.length - 1] + nums[nums.length - 2];
                if(twoSumTarget < minTwoSum || twoSumTarget > maxTwoSum) continue;
                for(int m = j+1,n= nums.length-1;m < n;){
                    int twoSum = nums[m] + nums[n];
                    if(twoSum < twoSumTarget){
                        while(m < n && nums[m] == nums[m+1]) m++;
                        m++;
                    }else if(twoSum > twoSumTarget){
                        while(m < n && nums[n] == nums[n-1]) n--;
                        n--;
                    }else{
                        List<Integer> tempList = new LinkedList<>(Arrays.asList(nums[i],nums[j],nums[m],nums[n]));
                        list.add(tempList);
                        while(m < n && nums[m] == nums[m+1]) m++;
                        m++;
                        while(m < n && nums[n] == nums[n-1]) n--;
                        n--;
                    }
                }
            }
        }
        return list;
    }
}

Search

    Table of Contents