滑动窗口之三数之和

三数之和

15. 三数之和 - 力扣(LeetCode) (leetcode-cn.com)

这个题最难的地方在于去重的实现。如果想实现去重,就必须对结果进行记录,还需要有一个比较的环节。

首先第一个解法是哈希+排序,很显然,炸了

public List<List<Integer>> threeSum(int[] nums) {
        int numsLen = nums.length;
        Map<Integer, List<Integer>> sum1_2 = new HashMap<>();
        Arrays.sort(nums);
        Set<List<Integer>> checkRes = new HashSet<>();
        List<List<Integer>> res =new ArrayList<>();
        for (int i = 0; i < numsLen; ++i) {
            for (int j = i + 1; j < numsLen; ++j) {
                List<Integer> temp = sum1_2.getOrDefault(nums[i] + nums[j],new ArrayList<>());
                temp.add(i);
                temp.add(j);
                sum1_2.put(nums[i] + nums[j],temp);
            }
        }
        for (int i = 0; i < numsLen; ++i) {
            if (sum1_2.containsKey(-nums[i])) {
                List<Integer> temp = sum1_2.get(-nums[i]);
                for (int j = 1; j < temp.size(); j+=2) {
                    int temp2 = temp.get(j);
                    if (i > temp2) {
                        int temp1 = temp.get(j - 1);
                        List<Integer> resTemp = new ArrayList<>();
                        List<Integer> checkString = new ArrayList<>();
                        checkString.add(nums[temp1]);
                        checkString.add(nums[temp2]);
                        checkString.add(nums[i]);
                        if (!checkRes.contains(checkString)) {
//                            resTemp.add(nums[temp1]);
//                            resTemp.add(nums[temp2]);
//                            resTemp.add(nums[i]);
                            res.add(checkString);
                            checkRes.add(checkString);
                        }

                    }
                }
            }
        }
        return res;
    }

解法二:排序加滑动窗口

这里去重的思路值得深入思考,本质上是固定一个,在其余的情况中找剩下的。

   public List<List<Integer>> threeSum(int[] nums) {
        List<List<Integer>> res = new ArrayList<>();
        Arrays.sort(nums); //对数组进行排序,求和时,窗口有序可以让滑动窗口变得有意义
        int numsLen = nums.length;
        for (int i = 0; i < numsLen; ++i) {
            int left = i + 1,right = numsLen - 1;
            while (left < right) {
                if (nums[i] + nums[left] + nums[right] == 0) { //找到一组解
//                    List<Integer> temp = new ArrayList<>();
//                    temp.add(nums[i]);
                     //加入集合
                    while(left < right && nums[left] == nums[left + 1]) { //和right等价的一切数都可以掠过不管
                        left++;
                    }
                    while(left < right && nums[right] == nums[right - 1]) {//和left等价的一切数都可以掠过不管
                        right--;
                    }
                    //移到下一处
                    res.add(new ArrayList<>(Arrays.asList(nums[i], nums[left], nums[right])));
                    left++;
                    right--;
                }
                else if (nums[i] + nums[left] + nums[right] > 0) { //总和太大,让右左移减小结果
                    while(left < right && nums[right] == nums[right - 1]) {//和left等价的一切数都可以掠过不管
                        right--;
                    }
                    right--;
                }
                else {
                    while(left < right && nums[left] == nums[left + 1]) { //和right等价的一切数都可以掠过不管
                        left++;
                    }
                    left++;
                }
            }
            while(i < numsLen - 1 && nums[i+1] == nums[i]) {++i; };
        }
        return res;
    }

18. 四数之和 - 力扣(LeetCode) (leetcode-cn.com)

这个题目是上面的题目的升级版。把三数之和升级为四数之和,基本思路是一样的,都是吧复杂度降低一个数量级。本身滑动窗口就避免了遍历,可以用来降数量级。

解法一:排序+滑动窗口

 public List<List<Integer>> fourSum(int[] nums, int target) {
        Arrays.sort(nums);
        int numsLen = nums.length;
        List<List<Integer>> res = new ArrayList<>();
        for (int i = 0; i < numsLen; ++i) {
            for (int j = i + 1 ; j < numsLen; ++j) {
                int left = j + 1,right = numsLen - 1;
                while( left < right) {
                    if (nums[i] + nums[j] + nums[left] + nums[right] == target) {
                        res.add(new ArrayList<>(Arrays.asList(nums[i], nums[j], nums[left], nums[right])));
                        while (left < right && nums[left + 1] == nums[left]) {
                            ++left;
                        }
                         while (left < right && nums[right - 1] == nums[right]) {
                            --right;
                        }
                        ++left;
                        --right;
                    }
                    else if (nums[i] + nums[j] + nums[left] + nums[right] < target) {
                        while (left < right && nums[left + 1] == nums[left]) {
                            ++left;
                        }
                        ++left;
                    }
                    else {
                         while (left < right && nums[right - 1] == nums[right]) {
                            --right;
                        }
                        --right;
                    }
                }
                 while ( j < numsLen - 1 &&  nums[j] == nums[j + 1]) {
                    ++j;
                }
            }
            while ( i < numsLen - 1 &&  nums[i] == nums[i + 1]) {
                ++i;
            }
        }
        return res;
    }

这里需要注意,看四个数相加的范围,很可能会超过long 的范围,需要考虑将变量类型定义为long或者把加法转换成减法来对待。

如果是五数之和,六数之和,也是一样的解法。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值