4Sum — LeetCode[通俗易懂]

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的问题。基本思想就是和另外一种方法一样进行二分。然后两两结合,只是细节就非常复杂了(这点从上面的另外一种解法就能看出来),所以不是非常适合在面试中出现。有兴趣的朋友能够进一步思考或者搜索网上材料哈。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/116177.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 三菱modbus通讯实例 PLC如何设置_三菱plc网络通讯指令范例

    三菱modbus通讯实例 PLC如何设置_三菱plc网络通讯指令范例点击箭头处“工业之家”,选择“关注公众号”!三菱PLC控制机械手实例气动机械手动作示意图,其功能是将工件从A处移送到B处。气动机械手的升降和左右移行分别使用了双线圈的电磁阀,在某方向的驱动线圈失电时能保持在原位,必须驱动反方向的线圈才能反向运动。上升、下降对应的电磁阀线圈分别是YV2、YV1,右行、左行对应的电磁阀线圈分别是YV3、YV4。机械手的夹钳使用单线圈电磁阀YV5,线圈通电时夹…

    2022年9月10日
    0
  • python中encode和decode的区别_python rindex

    python中encode和decode的区别_python rindex1.基本语法1.encode()和decode()都是字符串的函数decode解码encode编码str———>str(Unicode,byte类型)———>str2.decode()与encode()方法可以接受参数,其声明分别为:其中的encoding是指在解码编码过程中使用的编码(此…

    2022年10月6日
    0
  • Keil MDK 2020过期问题[通俗易懂]

    Keil MDK 2020过期问题[通俗易懂]KeilMDK2020过期问题由于到2020年过期,之前曾担心到2020年是否我们用KEILMDK所编写的代码,全部不可用。经过今天测试,虽然软件提示过期,不过依然可以正常使用,只是没有软件的支持维护而已。用微信扫描二维码为博主打个赏金额随意快来“打”我呀要买枸杞当归补补~~转自:https://www.zhjm.site/wordpress/?p=340…

    2022年5月9日
    200
  • 检查网站有没有被挂马_安全网址检测

    检查网站有没有被挂马_安全网址检测介绍很多网站都被挂过马,挂马即在获取服务器的部分权限或所有权限后,向网页文件中插入一段恶意代码,即挂马。这些恶意代码可以是浏览器漏洞的利用代码,也可以是赚取流量的代码,或者是盗取账号的代码。URLSnooperurlsnooper字面理解即url窥探,官方说明是可以帮助用户发现音频和视频文件的url地址。下载地址是http://www.donationcoder.com/softw…

    2022年9月30日
    0
  • 反转每对括号间的子串java_利用栈判断字符串括号是否匹配

    反转每对括号间的子串java_利用栈判断字符串括号是否匹配给出一个字符串 s(仅含有小写英文字母和括号)。请你按照从括号内到外的顺序,逐层反转每对匹配括号中的字符串,并返回最终的结果。注意,您的结果中 不应 包含任何括号。示例 1:输入:s = “(abcd)”输出:”dcba”示例 2:输入:s = “(u(love)i)”输出:”iloveu”示例 3:输入:s = “(ed(et(oc))el)”输出:”leetcode”示例 4:输入:s = “a(bcdefghijkl(mno)p)q”输出:”apmnolkjihgf

    2022年8月9日
    1
  • Android学习_ContentProvider和Uri

    Android学习_ContentProvider和Uri

    2022年2月1日
    48

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号