zoukankan      html  css  js  c++  java
  • 4Sum -- LeetCode

    原题链接: http://oj.leetcode.com/problems/4sum/ 
    这道题要求跟3Sum差点儿相同,仅仅是需求扩展到四个的数字的和了。我们还是能够依照3Sum中的解法,仅仅是在外面套一层循环。相当于求n次3Sum。我们知道3Sum的时间复杂度是O(n^2),所以假设这样解的总时间复杂度是O(n^3)。代码例如以下:
    public ArrayList<ArrayList<Integer>> fourSum(int[] num, int target) {
        ArrayList<ArrayList<Integer>> res = new ArrayList<ArrayList<Integer>>();
        if(num==null||num.length==0)
            return res;
        Arrays.sort(num);
        for(int i=num.length-1;i>2;i--)
        {
            if(i==num.length-1 || num[i]!=num[i+1])
            {
                ArrayList<ArrayList<Integer>> curRes = threeSum(num,i-1,target-num[i]);
                for(int j=0;j<curRes.size();j++)
                {
                    curRes.get(j).add(num[i]);
                }
                res.addAll(curRes);
            }
        }
        return res;        
    }
    private ArrayList<ArrayList<Integer>> threeSum(int[] num, int end, int target)
    {
        ArrayList<ArrayList<Integer>> res = new ArrayList<ArrayList<Integer>>();
        for(int i=end;i>1;i--)
        {
            if(i==end || num[i]!=num[i+1])
            {
                ArrayList<ArrayList<Integer>> curRes = twoSum(num,i-1,target-num[i]);
                for(int j=0;j<curRes.size();j++)
                {
                    curRes.get(j).add(num[i]);
                }
                res.addAll(curRes);
            }
        }
        return res;
    }
    private ArrayList<ArrayList<Integer>> twoSum(int[] num, int end, int target)
    {
        ArrayList<ArrayList<Integer>> res = new ArrayList<ArrayList<Integer>>();
        int l=0;
        int r=end;
        while(l<r)
        {
            if(num[l]+num[r]==target)
            {
                ArrayList<Integer> item = new ArrayList<Integer>();
                item.add(num[l]);
                item.add(num[r]);
                res.add(item);
                l++;
                r--;
                while(l<r&&num[l]==num[l-1])
                    l++;
                while(l<r&&num[r]==num[r+1])
                    r--;
            }
            else if(num[l]+num[r]>target)
            {
                r--;
            }
            else
            {
                l++;
            }
        }
        return res;
    }
    上述这样的方法比較直接。依据3Sum的结果非常easy进行推广。那么时间复杂度能不能更好呢?事实上我们能够考虑用二分法的思路,假设把全部的两两pair都求出来。然后再进行一次Two Sum的匹配。我们知道Two Sum是一个排序加上一个线性的操作,而且把全部pair的数量是O((n-1)+(n-2)+...+1)=O(n(n-1)/2)=O(n^2)。

    所以对O(n^2)的排序假设不用特殊线性排序算法是O(n^2*log(n^2))=O(n^2*2logn)=O(n^2*logn),算法复杂度比上一个方法的O(n^3)是有提高的。
    思路尽管明白,只是细节上会多非常多情况要处理。

    首先。我们要对每个pair建一个数据结构来存储元素的值和相应的index,这样做是为了后面当找到合适的两对pair相加能得到target值时看看他们是否有重叠的index,假设有说明它们不是合法的一个结果,由于不是四个不同的元素。接下来我们还得对这些pair进行排序。所以要给pair定义comparable的函数。最后。当进行Two Sum的匹配时由于pair不再是一个值,所以不能像Two Sum中那样直接跳过同样的。每一组都得进行查看,这样就会出现反复的情况,所以我们还得给每个四个元素组成的tuple定义hashcode和相等函数,以便能够把当前求得的结果放在一个HashSet里面,这样得到新结果假设是反复的就不增加结果集了。


    代码例如以下:

    private ArrayList<ArrayList<Integer>> twoSum(ArrayList<Pair> pairs, int target){
        HashSet<Tuple> hashSet = new HashSet<Tuple>();
        int l = 0;
        int r = pairs.size()-1;
        ArrayList<ArrayList<Integer>> res = new ArrayList<ArrayList<Integer>>();
        while(l<r){
            if(pairs.get(l).getSum()+pairs.get(r).getSum()==target)
            {
                int lEnd = l;
                int rEnd = r;
                while(lEnd<rEnd && pairs.get(lEnd).getSum()==pairs.get(lEnd+1).getSum())
                {
                    lEnd++;
                }
                while(lEnd<rEnd && pairs.get(rEnd).getSum()==pairs.get(rEnd-1).getSum())
                {
                    rEnd--;
                }
                for(int i=l;i<=lEnd;i++)
                {
                    for(int j=r;j>=rEnd;j--)
                    {
                        if(check(pairs.get(i),pairs.get(j)))
                        {
                            ArrayList<Integer> item = new ArrayList<Integer>();
                            item.add(pairs.get(i).nodes[0].value);
                            item.add(pairs.get(i).nodes[1].value);
                            item.add(pairs.get(j).nodes[0].value);
                            item.add(pairs.get(j).nodes[1].value);
                            //Collections.sort(item);
                            Tuple tuple = new Tuple(item);
                            if(!hashSet.contains(tuple))
                            {
                                hashSet.add(tuple);
                                res.add(tuple.num);
                            }
                        }                        
                    }
                }
                l = lEnd+1;
                r = rEnd-1;
            }
            else if(pairs.get(l).getSum()+pairs.get(r).getSum()>target)
            {
                r--;
            }
            else{
                l++;
            }
        }
        return res;
    }
    private boolean check(Pair p1, Pair p2)
    {
        if(p1.nodes[0].index == p2.nodes[0].index || p1.nodes[0].index == p2.nodes[1].index)
            return false;
        if(p1.nodes[1].index == p2.nodes[0].index || p1.nodes[1].index == p2.nodes[1].index)
            return false;
        return true;
    }
    另外一种方法比第一种方法时间上还是有提高的,事实上这道题能够推广到k-Sum的问题。基本思想就是和另外一种方法一样进行二分。然后两两结合,只是细节就非常复杂了(这点从上面的另外一种解法就能看出来),所以不是非常适合在面试中出现。有兴趣的朋友能够进一步思考或者搜索网上材料哈。
  • 相关阅读:
    Mysql8.0中caching_sha2_password报错解决
    Centos7 安装mysql-8.0.18(rpm)
    如何有效的清理yum缓存
    虚拟机安装后配置IP地址
    正确计算linux系统内存使用率
    Linux 怎样更改locale语言设置
    linux把EDT时间修改为CST格式
    开放接口的安全验证方案(AES+RSA)
    Linux下JDK中文字体乱码
    服务器http请求https服务时报错解决方案
  • 原文地址:https://www.cnblogs.com/yxwkf/p/5159419.html
Copyright © 2011-2022 走看看