LeetCode从两数之和到K数之和

这篇博客详细介绍了从LeetCode的两数之和问题扩展到K数之和的解题思路。博主探讨了如何处理重复集合和多层遍历的关键点,并提供了针对三数之和、三数最接近目标和、四数之和的解决方案。文章强调了排序、使用map存储已遍历数字的重要性,以及在处理溢出和去重问题时的注意事项。最后,博主提出了对于更高维度的K数之和问题,需要更有效的回溯策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

LeetCode上第一题关于两数之和的问题,从而扩展到3数之和,4数之和等,其中关于解决重复集合的问题是关键以及多层遍历是解题的关键,以下归纳并总结其中在解题的过程中遇到的问题。

1.两数之和

https://leetcode.com/problems/two-sum/
这是leetcode的第一道题,也是入门简单等级。其中不包含重复数字也只有一组唯一解,相对来说只要从一个下标的数字开始并从当前的下一个开始到结束找到有符合的即为结果。
注意:这里的数字范围包含负数,目标值也可能为负数,因此不能采用传统双指针的方式去解决。

最简单的方式即采用map存储出现过的数字及对应下标,map的get为O(1)。

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

而适用于双指针的场景,左指针往后一定是结果变大,右指针往前一定是结果变小。

15.三数之和

https://leetcode.com/problems/3sum/
从两个数字扩展到三个数字,并且不包含重复方案,且元素下标也不相等,但目标和为0。依然可以利用两数之和的方式对每个元素找与之对应的相反数是否存在,并跳过连续相等的元素。
同时对于多于两个数的情况,需要先对数组进行排序,这样才可以保证从左往右或从右往左根据数据大小进行范围的缩小。
注意:约束中,数值的范围代表用和的方式不会存在int的溢出,因此不需要考虑用long

  • -10^5 <= nums[i] <= 10^5
class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
      List<List<Integer>> result = new ArrayList<>();
        if (nums == null || nums.length < 3) {
            return result;
        }
        Arrays.sort(nums);
        if(nums[0]>0){
            return result;
        }
        for (int i = 0; i < nums.length - 2;i++) {
            twoSum(nums,i,result);
            while (i+1<nums.length&& nums[i]==nums[i+1]){
                i++;
            }
        }
        return  result;
    }

    public static void twoSum(int[] nums, int i, List<List<Integer>> list) {
        int left = i + 1, right = nums.length - 1;
        while (left < right) {
            int sum = nums[i] + nums[left] + nums[right];
            if (sum > 0) {
                right--;
            } else if (sum < 0) {
                left++;
            } else {
                list.add(Arrays.asList(nums[i],nums[left],nums[right]));
                while (left+1<right && nums[left] == nums[left+1]){
                    left++;
                }
                while (left <right-1 && nums[right] == nums[right-1]){
                    right--;
                }
                left++;
                right--;
            }
        }
    }
}

16.三数最接近目标和

https://leetcode.com/problems/3sum-closest/
这个与前面三数之和的问题比较相近,唯一不同是条件变为与target相差最小(包括与target相等)三数之和的值
同理,超过两数需要先对数组进行排序。并以某个元素开始,从它的下一个与数组最后一个的范围开始搜索。
约束条件中依然表示不会存在溢出的情况,可以不用考虑

  • -1000 <= nums[i] <= 1000
  • -10^4 <= target <= 10^4
class Solution {
    public int threeSumClosest(int[] nums, int target) {
      Arrays.sort(nums);
        int[] res = new int[]{target > 0 ? Integer.MIN_VALUE : Integer.MAX_VALUE};
        for (int i = 0; i < nums.length-2; i++) {
            if (i > 0 && nums[i] == nums[i - 1]) {
                continue;
            }
             twoSum(nums, i, target,res);
            if(res[0] == target){
                return target;
            }
        }
        return res[0];
    }
    
   public static void twoSum(int[] nums, int index, int target,int[] result) {
        int left = index + 1, right = nums.length - 1;

        while (left < right) {
            int sum = nums[index] + nums[left] + nums[right];
            if (sum == target) {
                result[0] = target;
                return ;
            }
            if (Math.abs(sum - target) < Math.abs(result[0] - target)) {
               result[0] = sum;
            }
            if (sum > target) {
                right--;
            } else   {
                left++;
            }
        }

    }
}

18.四数之和

https://leetcode.com/problems/4sum/
关键信息在于:四数、方案不重复、等于目标和、数值范围可能溢出等。在三数之和的基础上,我们如果解决溢出、叠加遍历并去重依然可以用相似的方案。

  • -10^9 <= nums[i] <= 10^9
  • -10^9 <= target <= 10^9

固定第一个元素,在依次选择下一个作为第二元素,并求解往后剩下的范围内与前两数组成的满足条件的方案。

class Solution {
    public List<List<Integer>> fourSum(int[] nums, int target) {
        List<List<Integer>> list=new ArrayList<>();
        Arrays.sort(nums);
        for(int i=0;i<nums.length-3;i++){
            if(i>0 && nums[i]==nums[i-1]){
                continue;
            }
            for(int j=i+1;j<nums.length-2;j++){
                if(j>i+1 && nums[j]==nums[j-1]){
                    continue;
                }
                int left = j+1,right=nums.length-1;
                long remain= (long)target -nums[i]-nums[j];
                while(left<right){
                    long curSum = nums[left]+nums[right];
                    if(curSum==remain){
                        list.add(Arrays.asList(nums[i],nums[j],nums[left],nums[right]));

                        while(left+1<right && nums[left]==nums[left+1]){
                            left++;
                        }
                        left++;
                    }else if(curSum<remain){
                        left++;
                    }else{
                        right--;
                    }
                }

            }
        }
        return list;
    }
}

到四数后,我们发现需要叠加的遍历层数开始变的多了起来,需要O(n^3)。那如果还要更多的K数该怎么做呢?

K数之和

从四数之和的解法来看,需要对更多个数字的和这类问题有较好的回溯方式,否则只能无限的叠加遍历。

class Solution {
    public List<List<Integer>> fourSum(int[] nums, int target) {
        List<List<Integer>> list=new ArrayList<>();
        if(nums.length<4){
            return list;
        }
        Arrays.sort(nums);
        list  =kSum(nums,target,4,0,nums.length);

        return list;
    }

    public List<List<Integer>> kSum(int[] nums, long target,int k,int begin,int end) {
        List<List<Integer>> list=new ArrayList<>();
        if(begin>= end){
            return list;
        }
        if(k==2){
            return twoSum(nums,target,begin,end);
        }
        for (int i = begin; i <= end-k; ++i) {
            if (i > begin && nums[i - 1] == nums[i]) {
               continue;
            }
            List<List<Integer>> temp = kSum(nums, target - nums[i], k - 1, i + 1, end);
            for (List<Integer> it : temp) {
                List<Integer> newList = new ArrayList<>(Arrays.asList(nums[i]));
                newList.addAll(it);
                list.add(newList);
            }
        }
        return list;
    }

    public List<List<Integer>> twoSum(int[] nums,long target,int start,int end){
         List<List<Integer>> res = new ArrayList<>();
        int lo = start, hi = end - 1;
        
        while (lo < hi) {
            long currSum = nums[lo] + nums[hi];
            if (currSum < target || lo > start && nums[lo] == nums[lo - 1]) {
                ++lo;
            } else if (currSum > target || hi < end - 1 && nums[hi] == nums[hi + 1]) {
                --hi;
            } else {
                res.add(Arrays.asList(nums[lo], nums[hi]));
                lo++;
                hi--;
            }
        }
        return res;
    }
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值