zoukankan      html  css  js  c++  java
  • 4Sum

    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
  • 相关阅读:
    ASP.NET MVC + EF 利用存储过程读取大数据,1亿数据测试很OK
    ASP.NET MVC+EasyUI+Entity FrameWork 整合开发
    ASP.NET MVC局部验证及相关问题
    asp.net mvc常用的数据注解和验证以及entity framework数据映射
    Entity Framework 一次加载许多个 Fluent API 映射
    Entity Framework Code First 常用方法集成
    ASP.NET MVC 应用程序的安全性,看一眼你就会了
    Entity Framework SqlFunctions 教你如何在EF调用sqlserver方法的函数存根
    ASP.NET MVC 及 Areas 简单控制路由
    ASP.NET MVC 表单的几种提交方式
  • 原文地址:https://www.cnblogs.com/hf-cherish/p/4619929.html
Copyright © 2011-2022 走看看