高频7. 三数之和 及其变形题的各种解法 Java codetop 递归 两数之和 n数之和

题目描述

三数之和

给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有和为 0 且不重复的三元组。

注意:答案中不可以包含重复的三元组。

示例 1:

输入:nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2],[-1,0,1]]

示例 2:

输入:nums = []
输出:[]

示例 3:

输入:nums = [0]
输出:[]

各种解法

1. 排序+双指针+两次去重

class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        Arrays.sort(nums);
        List<List<Integer>> res = new ArrayList<>();
        // k是第一个数,保持不动
        for(int k=0; k< nums.length-2; k++){
            // 最小的都大于0,没有结果
            if(nums[k] > 0) break;
            // 和前一个数一样,有也是重复,不考虑
            if(k>0 && nums[k]==nums[k-1]) continue;
            // 双指针
            int i=k+1, j=nums.length-1;
            while(i<j){
                int sum = nums[k]+nums[i]+nums[j];
                // 和偏小,左指针向右
                if(sum<0){
                    while (i<j && nums[i] == nums[++i]);
                }else if(sum > 0){
                    while (i<j && nums[j] == nums[--j]);
                }else{
                    res.add(new ArrayList<Integer>(Arrays.asList(nums[k],nums[i],nums[j])));
                    while(i<j && nums[i] == nums[++i]);
                    while (i<j && nums[j] == nums[--j]);
                }
            }
        }
        return res;
    }
}
解题思路
  • 排序:首先将数组进行排序,排完序有了大小关系,才能用双指针来找。如果和大了,右指针向左到一个较小的值;如果和小了,左指针向右到一个较大的值。
  • 双指针:固定最左的指针最小,双指针在后面的区域之间移动。也就是固定1个数,然后就是用双指针求另外两个数的【两数之和】问题。
  • 记录对于每个固定指针k的所有满足 nums[k] + nums[i] + nums[j] == 0 的 i,j 组合:
    • 当 nums[k] > 0 时直接break跳出:因为 nums[j] >= nums[i] >= nums[k] > 0,即 3 个数字都大于 0,在此固定指针 k 之后不可能再找到结果了。
    • 【第一次去重】:当 k > 0且nums[k] == nums[k - 1]时即跳过此元素nums[k]:因为已经将 nums[k - 1] 的所有组合加入到结果中,本次双指针搜索只会得到重复组合。
    • i,j 分设在数组索引 (k, len(nums))(k,len(nums)) 两端,当i < j时循环计算s = nums[k] + nums[i] + nums[j],并按照以下规则执行双指针移动【第二次去重】:
      • 当s < 0时,i += 1并跳过所有重复的nums[i];
      • 当s > 0时,j -= 1并跳过所有重复的nums[j];
      • 当s == 0时,记录组合[k, i, j]至res,执行i += 1和j -= 1并跳过所有重复的nums[i]和nums[j],防止记录到重复组合。
  • 如果没有两次去重很可能超时。
  • 排序O(nlogn),双指针O(n^2)

变形题

1.两数之和

分为两种情况

1.1 排好序的,双指针

排好序的两数之和,可以直接使用 双指针 解法,逻辑和上面的类似

int[] twoSum(int[] nums, int target) {
    int left = 0, right = nums.length - 1;
    while (left < right) {
        int sum = nums[left] + nums[right];
        if (sum == target) {
            return new int[]{left, right};
        } else if (sum < target) {
            left++; // 让 sum 大一点
        } else if (sum > target) {
            right--; // 让 sum 小一点
        }
    }
    // 不存在这样两个数
    return new int[]{-1, -1};
}

1.2 未排序的,HashMap一次遍历

如果要求时间复杂度为O(N),那么不可能再去排序了, 因为排序最优情况下都是 O(NlogN)。所以使用HashMap一次遍历,在遍历中记录信息。

class Solution {
    public int[] twoSum(int[] nums, int target) {
        Map<Integer, Integer> map = new HashMap<>();
        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];
    }
}
解题思路
  • 在遍历的过程中,把值和自己的索引记录到HashMap中
  • 如果HashMap中存在与自己相加为指定值的数,就成对输出

2. nSum问题

  • 只要是排好序的数组,两数之和就可以使用双指针。
  • 三数之和考虑对第一个数进行穷举,选出第一个后,对剩下的进行2sum解决。注意过程中的2次去重
  • 扩展到nSum,那么如果n>2,就是递归计算(n-1)Sum的结果。第一个数穷举,后面的数(n-1)Sum。递归终止条件是2sum问题,可以直接用双指针解出来。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值