码迷,mamicode.com
首页 > 其他好文 > 详细

4Sum

时间:2015-07-14 11:15:43      阅读:202      评论:0      收藏:0      [点我收藏+]

标签:

1. Question

给定整型数组和target,找数组中的所有和为target的四个数,将其按值升序排列输出,输出结果不包含重复数对。

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)

2. Solution

2.1 O(n3)

利用3SUM2Sum,这可以作为所有KSum的解法:

  • 数组排序
  • i:第i个数是否在某个四数对中,i初值为len-1;
    • 在。在剩下的数中找满足条件的3Sum
    • 不在。删除该数。
技术分享
public class Solution {
    //use threeSum, O(n3)
    public List<List<Integer>> fourSum( int[]nums, int target ){
        List<List<Integer>> res = new ArrayList<List<Integer>>();
        int len = nums.length;
        if( len<4 )
            return res;
        Arrays.sort( nums );
        Set<List<Integer>> testUnique = new HashSet<List<Integer>>();
        for( int i=len-1; i>=3; i-- ){
            for( int j=0; j<=i-3; j++ ){
                int twoSum = target- nums[i] - nums[j];
                for( int k=j+1, p=i-1; k<p; ){
                    int nowTwo = nums[k] + nums[p];
                    if( nowTwo < twoSum )
                        k++;
                    else if( nowTwo > twoSum )
                        p--;
                    else{
                        List<Integer> newFour = new ArrayList<Integer>();
                        newFour.add( nums[j] );
                        newFour.add( nums[k] );
                        newFour.add( nums[p] );
                        newFour.add( nums[i] );
                        if( testUnique.add(newFour) )
                            res.add( newFour );
                        k++;
                        p--;
                    }
                }
            }
        }
        return res;
    }
}
View Code

上述方法可以改进,即对其中某些选项剪枝,效率提高很多,但最坏情况依然是O(n3)

  • a, b, c, d:四数对
  • maxDWhenAIncrease: 当a, b, c为最小可能值时,如果a+b+c+maxDWhenAIncrease > target,则maxDWhenAIncrease--
  • maxDWhenBIncrease:当a, b, c为最小可能值时,如果a+b+c+maxDWhenBIncrease > target,则maxDWhenBIncrease--
  • 剪枝:
    • 重复数:当前a/b和上一次a/b相同,无需重复计算;
    • a太小或太大:
      • 太小:a和当前b, c, d的最大可能值之和 < target
      • 太大:a和当前b, c, d的最小可能值之和 > target
    • b太小或太大(此时a已确定):
      • 太小:a+b和当前c, d的最大可能值之和 < target
      • 太大:a+b和当前c, d的最小可能值之和 > target

 

技术分享
    //improved solution using threeSum. worst time O(n3)
    public List<List<Integer>> fourSum( int[] nums, int target ){
        List<List<Integer>> res = new ArrayList<List<Integer>>();
        int len = nums.length;
        if( len<4 )
            return res;
        
        int maxDWhenAIncrease = len-1;
        int maxDWhenBIncrease;
        Arrays.sort( nums );
        
        for( int i=0; i<=maxDWhenAIncrease-3; i++ ){
            //remove duplicates and prune when a is too small
            if( ( i>0 && nums[i]==nums[i-1] ) || ( nums[i] + nums[maxDWhenAIncrease-2] + nums[maxDWhenAIncrease-1] + nums[maxDWhenAIncrease] < target ) )
                    continue;
            //stop when a is too big
            if( nums[i] + nums[i+1] + nums[i+2] + nums[i+3] > target )
                break;
            
            //update maxDWhenAIncrease & maxDWhenBIncrease
            while( nums[i] + nums[i+1] + nums[i+2] + nums[maxDWhenAIncrease] > target )
                maxDWhenAIncrease--;
            maxDWhenBIncrease = maxDWhenAIncrease;
            
            for( int j=i+1; j <= maxDWhenBIncrease-2; j++ ){
                //remove duplicates and prune when b is too small
                if( ( j>i+1 && nums[j] == nums[j-1] ) || ( nums[i] + nums[j] + nums[maxDWhenBIncrease-1] + nums[maxDWhenBIncrease] < target) )
                    continue;
                //stop when b is too big
                if( nums[i] + nums[j] + nums[j+1] + nums[j+2] > target )
                    break;
                //update maxDWhenBIncrease
                while( nums[i] + nums[j] +nums[j+1] + nums[maxDWhenBIncrease] > target )
                    maxDWhenBIncrease--;
                
                int remainSum = target - nums[i] - nums[j];
                //use two pointers to find the remain two numbers
                for( int m=j+1,n=maxDWhenBIncrease; m<n; ){
                    int nowSum = nums[m] + nums[n];
                    if( nowSum < remainSum )
                        while( (++m) < n && nums[m] == nums[m-1] );
                    else if( nowSum > remainSum )
                        while( (--n) > m && nums[n] == nums[n+1] );
                    else{
                        List<Integer> newFour = new ArrayList<Integer>();
                        newFour.add( nums[i] );
                        newFour.add( nums[j] );
                        newFour.add( nums[m] );
                        newFour.add( nums[n] );
                        res.add(newFour);
                        while( (++m) < n && nums[m] == nums[m-1] );
                        while( (--n) > m && nums[n] == nums[n+1] );
                    }
                }
            }
        }
        
        return res;
    }
View Code

 

2.2 O(n4)

利用2Sum,先求出所有双数对和,然后遍历和求符合要求的数对(2sum)。

占用内存过多,效果不好。

技术分享
    //improved solution of O(n2) time
    public List<List<Integer>> fourSum3( int[] nums, int target ){
        List<List<Integer>> res = new ArrayList<List<Integer>>();
        int len = nums.length;
        if( len<4 ) 
            return res;
        
        Arrays.sort(nums);
        
        HashMap<Integer, List<List<Integer>>> twoSums = new HashMap<Integer, List<List<Integer>>>();
        //calculate all the two sum pairs
        for( int i=0; i<len-1; i++ )
            for( int j=i+1; j<len; j++ ){
                List<Integer> newNowSumCouple = new ArrayList<Integer>();
                newNowSumCouple.add( i );
                newNowSumCouple.add( j );
                List<List<Integer>> newNowSumList = new ArrayList<List<Integer>>();
                int nowSum = nums[i] + nums[j];
                if( twoSums.containsKey( nowSum ) ){
                    newNowSumList = twoSums.get( nowSum );
                    newNowSumList.add( newNowSumCouple );
                    twoSums.replace( nowSum, newNowSumList);
                }
                else{
                    newNowSumList.add( newNowSumCouple );
                    twoSums.put( nowSum, newNowSumList);                
                }
            }
        //traverse the twoSums map to find the satisfied quadruples 
        for( Map.Entry<Integer, List<List<Integer>>> twoSum: twoSums.entrySet() ){
            int sum1 = twoSum.getKey();
            int sum2 = target - sum1;
            
            if( !twoSums.containsKey(sum2) )
                continue;
            
            List<List<Integer>> sumList1 = twoSum.getValue();
            List<List<Integer>> sumList2 = twoSums.get( sum2 );
            for( int i=0; i<sumList1.size(); i++ ){
                int  a = nums[sumList1.get(i).get(0)];
                int b = nums[sumList1.get(i).get(1)];
                //remove duplicates in sumList1
                if( i==0 || a != nums[sumList1.get(i-1).get(0)])
                    for( int j=sumList2.size()-1; j>=0; j-- ){
                        int c = nums[sumList2.get(j).get(0)];
                        int d = nums[sumList2.get(j).get(1)];
                        //remove duplicates in sumList2 and only keep the quadruples that a<=b<=c<=d (check through the index since the array is sorted)
                        if( sumList1.get(i).get(1) < sumList2.get(j).get(0) && (j==sumList2.size()-1 || c != nums[sumList2.get(j+1).get(0)]) ){
                            List<Integer> newFour = new ArrayList<Integer>();
                            newFour.add(a);
                            newFour.add(b);
                            newFour.add(c);
                            newFour.add(d);
                            res.add(newFour);
                        }
                    }
            }
        }
        
        return res;
    }
View Code

 

4Sum

标签:

原文地址:http://www.cnblogs.com/hf-cherish/p/4619929.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!