nSum问题的统一解法

经常刷 LeetCode 的读者肯定知道鼎鼎有名的 twoSum 问题,我们上篇文章 twoSum 问题的核心思想 就对 twoSum 的几个变种做了解析。
但是除了 twoSum 问题,LeetCode 上面还有 3Sum,4Sum 问题,我估计以后出个 5Sum,6Sum 也不是不可能。
那么,对于这种问题有没有什么好办法用套路解决呢?本文就由浅入深,层层推进,用一个函数来解决所有 nSum 类型的问题。

一、twoSum 问题

返回数组下标

给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那两个整数,并返回它们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。
你可以按任意顺序返回答案。


注意事项

  1. 千万不能排序,否则下标就不对了

只有且仅有一对

如果假设输入一个数组 nums 和一个目标和 target,请你返回 nums 中能够凑出 target 的两个元素的值,比如输入 nums = [1,3,5,6], target = 9,那么算法返回两个元素 [3,6]。可以假设只有且仅有一对儿元素可以凑出 target。

我们可以先对 nums 排序,然后利用前文 双指针技巧 写过的左右双指针技巧,从两端相向而行就行了:

public List<List<Integer>> twoSumTarget(int[] nums, int target) {
   
    // 先对数组排序
    Arrays.sort(nums);
    // 双指针的方式查找
    int lo = 0;
    int hi = nums.length - 1;
    while (lo < hi) {
   
        int sum = nums[lo] + nums[hi];
        // 根据 sum 和 target 的比较,移动左右指针
        if(sum < target) {
   
          lo++;
        }else if (sum > target) {
   
          hi--;
        }else {
   
            return Arrays.asList(nums[lo],nums[hi]);
        }
    }
    return new ArrayList<>();
}

多对且不能重复

这样就可以解决这个问题,不过我们要继续魔改题目,把这个题目变得更泛化,更困难一点:
nums 中可能有多对儿元素之和都等于 target,请你的算法返回所有和为 target 的元素对儿,其中不能出现重复。
函数签名如下:
vector<vector> twoSumTarget(vector& nums, int target);
比如说输入为 nums = [1,3,1,2,2,3], target = 4,那么算法返回的结果就是:[[1,3],[2,2]]。

对于修改后的问题,关键难点是现在可能有多个和为 target 的数对儿,还不能重复,比如上述例子中 [1,3] 和 [3,1] 就算重复,只能算一次。
首先,基本思路肯定还是排序加双指针:
但是,这样实现会造成重复的结果,比如说 nums = [1,1,1,2,2,3,3], target = 4,得到的结果中 [1,3] 肯定会重复。
出问题的地方在于 sum == target 条件的 if 分支,当给 res 加入一次结果后,lo 和 hi 不应该改变 1 的同时,还应该跳过所有重复的元素:
所以,可以对双指针的 while 循环做出如下修改:

public List<List<Integer>> twoSumTarget(int[] nums, int target) {
   
    // 先对数组排序
    Arrays.sort(nums);
    // 双指针的方式查找
    List<List<Integer>> res = new ArrayList<>();
    int lo = 0;
    int hi = nums.length - 1;
    while (lo < hi) {
   
        int sum = nums[lo] + nums[hi];
        // 根据 sum 和 target 的比较,移动左右指针
        if(sum < target) {
   
          lo++;
        }else if (sum > target) {
   
          hi--;
        }else {
   
          res.add(Arrays.asList(nums[lo],nums[hi]));
          // 跳过所有重复的元素
          while (lo < hi && nums[lo
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值