n个数之和的问题

两数之和

这是力扣算法题里面的第一道题,当你点开评论区:有人相爱,有人夜里开车看海,有人leetcode第一题都做不出来。哈哈哈!这道题毕竟是个简单题难到是不难,但是我们该如何给他写的更好?

看到题的第一眼,很容易想到的一种解法是暴力双循环,两次循环两两相加,满足要求就输出。好!那这里就先暴力解法代码比较简单就不写代码了,一运行,好家伙80ms直接垫底。这怎么能行,必须再想个解法!

双循环的时间复杂度是o(n^2),如何降低既能他的时间复杂度又能查询呢?低耗时的查询方法还能有谁?那必然是哈希表啊!这里我们在遍历数组时判断哈希表里是否有对应的值,如果没有就讲与之对应的另一半放入哈希表中,key是(targer-num[i]),value是i。这样只需要遍历一次直接将时间复杂度从o(n^2)降低到了o(1)。看代码:

public int[] twoSum(int[] nums, int target) {
        HashMap<Integer,Integer> map = new HashMap<Integer,Integer>();
        for(int i=0;i<nums.length;i++){
            if(map.containsKey(target-nums[i])){
                return new int[]{map.get(target-nums[i]),i};
            }
            map.put(nums[i],i);
        }
        return new int[0];
    }

运行一看,1ms!!这不比暴力解法快的多,哈希赶紧学起来。

三数之和

“这个我知道,我知道三层for循环不就搞定了嘛,虽然多好花点时间嘛!”嘿嘿!上一题投机取巧也就算了,这一题leetcode可不惯着你,暴力解法直接超时不能用。那我就用哈希,可是三个数哈希怎么存呢?但是我可以先固定一个数呀对吧,第一次遍历的值就是上一题的target,后续两个数相加等于-target就行啦。就相当于给上面一题的外面在套一层for循环。

但是,这一题难度可不止这些,难点在于不能有重复的三元组,数组中的数可以重复,但是输出的列表值要唯一。这个去重可是花费了我很长时间,最后我也没想到。我们一起先来看看代码吧:

public List<List<Integer>> threeSum(int[] nums) {
        List<List<Integer>> listNum = new ArrayList<List<Integer>>();
        int n = nums.length;
        //升序排序(必须排序,不然没法去重)
        Arrays.sort(nums);
        int first;
        int second;
        for(int i= 0;i<n;i++){
            if(i>0 && nums[i]==nums[i-1]){
                continue;
            }
            int num = -nums[i];
            int h = n-1;
            for(int j=i+1;j<n;j++){
                //去重操作
                if(j>i+1 && nums[j]==nums[j-1]){
                    continue;
                }
                while(h > j && nums[j]+nums[h]>num){
                    h--;
                }
                if(h==j){
                    break;
                }
                if(nums[j]+nums[h]==num){
                    List list = new ArrayList();
                    list.add(nums[i]);
                    list.add(nums[j]);
                    list.add(nums[h]);
                    listNum.add(list);
                }
            }
        }
        return listNum;
    }

这里我用的是双指针的方法,你看着可能像是三循环嵌套,可他比三循环嵌套花费是时间要少很多。大致是这样的,第一层循环从0开始逐个获得数组的值,第二层指针j从第一次循环的下标加1处开始向后移动,第三层指针h从数组尾部开始从后向前遍历,当j=h时结束这层的便利。可以参考下图:

那关键在于如何去重呢?去重意味着相同值的数据我只需要循环一遍就行,例如i=0时值为-1,i=4时值也是-1这层循环就可以直接跳过就行,j也是一样。这里的切入点在于 Arrays.sort(nums);对!排序,要想去重我们要先排序,如果数组是这样[-4,-1,-1,0,1,2]的话是不是就明了啦?此时我只需要在当num[i]=num[i]-1时跳出当次循环即可。但是代码里还写了另一个条件i>0,这是为什么?这个例子可能你们无法理解这一点,换个例子当数组是[0,0,0,0]时循环直接一次不走了全跳过了,这可不是我们想要的。加上限制条件确保了即使你就算有相同的值你也必须先给我把第一遍走了再跳过。

这个题的难点在于如何处理去重,要计入小本本:给数组去重可以先排序再去重。算法技巧又+1。

三数之和就有了那我再来个四数之和不过分吧😏!Leetcode 18题,我还没做但我想着是在三数之和上再套一层循环,不过感觉会超时!那如果四个数之和的四个数分别在四个数组中又该怎么办?Leetcode 454题。真是要命😨,大家可以去挑战一下。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值