每日一题: 三数之和

92 阅读2分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第19天,点击查看活动详情

三数之和

力扣题目链接

给你一个包含 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]
输出: []

 

提示:

  • 0 <= nums.length <= 3000
  • -105 <= nums[i] <= 105

解题思路:哈希表

这道题可以使用哈希解法

题目要求 a + b + c = 0c = -(a + b).我们可以每次枚举ab,在数组里寻找c是否存在

通过两层for循环来枚举ab的值,用哈希通过O(1)的时间在数组里取寻找-(a+b)

虽然思路是正确的,但是我们还会遇到一个问题,题目要求答案不可以包含重复的三元组。我们不光要找出正确的三元组,还需要把结果集进行去重,难点就在这去重上,我直接上代码,代码里有注释,帮助大家更好的理解

代码:(JAVA实现)

public static List<List<Integer>> threeSum(int[] nums) {
    //如果数组里的元素个数 < 3,就不需要往下进行了,直接返回
    if (nums.length < 3) {
        return new ArrayList<List<Integer>>();
    }
    
    //排序,方便我们去重
    Arrays.sort(nums);

    
    List<List<Integer>> list = new ArrayList<>();

    
    for (int i = 0; i < nums.length;i++) {
        
        //排序之后nums的第一个数如果 > 0,也不需要往下进行了,根本无法凑成三元组
        if (nums[i] > 0) {
            break;
        }

        //元素的去重
        if (i > 0 && nums[i] == nums[i-1]) {
            continue;
        }

        //用set来存储-(a+b)这个数
        HashSet<Object> set = new HashSet<>();
        
        for (int j = i+1; j < nums.length;j++) {
            //这步也是为了去除重复元素
            if (j > i+2
                    && nums[j] == nums[j-1]
                    && nums[j] == nums[j-2]) {
                continue;
            }

            //如果nums[j]不在数组里,就把[-a-b]添加到set里
            if (!set.contains(nums[j])) {
                set.add(-nums[i]-nums[j]);

            }else {
                
                List<Integer> res = new ArrayList<>();

                
                res.add(nums[i]);
                res.add(nums[j]);
                res.add(-nums[i]-nums[j]);
                list.add(res);
                set.remove(-nums[i]-nums[j]);

                //还是去重操作
                while (j < nums.length-1 && nums[j] == nums[j+1]) {
                    j++;
                }
            }
        }
    }
    //返回
    return list;
}

复杂度分析

  • 时间复杂度: O(n^2)
  • 空间复杂度: O(1)

提交结果

image.png

解题思路:排序+双指针

其实本题更适合用双指针来做,因为哈希的去重操作太麻烦了,如果刚做这道题的人,想到了用哈希的思路,也不一定能把去重操作写出来,所以我还是比较喜欢用双指针来做这道题,更直观,也更简单些

思路其实很简单:

  1. 首先将数组先排序,也是方便我们进行去重操作
  2. 定义一个for循环,i从数组下标0的地方开始,同时再定义左右指针,左指针left在i+1的位置,右指针rightnums.length-1的位置
  3. 每次计算这三个数 nums[i] + nums[left] + nums[right] 的和sum,是否满足0,满足添加到结果集里
  4. 如果nums[i] == nums[i-1],结果会重复,应该跳过
  • sum > 0,说明这个数大了,需要移动right,因为已经排序过的关系,越靠左的数越小,right就应该向左移动一格
  • sum < 0,说明这个数小了,left向右移动一格
  • sum == 0,这个数刚好满足0,将结果添加到结果集里
  • 当sum == 0 时,如果 nums[left] == nums[left+1] 则会导致结果重复,应该跳过,left++
  • 当sum == 0 时,如果 nums[right] == nums[right-1] 则会导致结果重复,应该跳过,right--

代码:(JAVA实现)

public static List<List<Integer>> threeSum(int[] nums) {
    //如果数组里的元素个数 < 3,就不需要往下进行了,直接返回
    if (nums.length < 3) {
        return new ArrayList<List<Integer>>();
    }

    List<List<Integer>> list = new ArrayList<>();

    //排序
    Arrays.sort(nums);

    //确定一个数组,将三数之和转换成两数之和
    for (int i = 0; i < nums.length; i++) {
        
        //当nums的第一个元素代表后面的元素无法构成要求的三元组,直接跳过即可
        if (nums[i] > 0) {
            break;
        }
        
        //先确定第一位数字,去寻找剩下的两个数字的组合,当第一位数字重复时代表在之前就已经枚举过了,需要去除
        if (i > 0 && nums[i] == nums[i-1]) {
            continue;
        }

        //设置左右指针,左右指针一定要比第一个数大
        int left = i+1;
        int right = nums.length-1;

        
        while (right > left) {
            int sum = nums[i]+nums[left]+nums[right];

            if (sum < 0) {
                left++;

            }else if(sum > 0) {
                right--;

            }else {
                List<Integer> res = new ArrayList<>();
                
                res.add(nums[i]);
                res.add(nums[left]);
                res.add(nums[right]);
                list.add(res);

                while (right > left && nums[left] == nums[left+1]) {
                    left++;
                }

                while (right > left && nums[right] == nums[right-1]) {
                    right--;
                }

                //缩小指针范围
                left++;
                right--;
            }

        }

    }
    return list;
}

复杂度分析

  • 时间复杂度:O(n^2)
  • 空间复杂度:O(1)

提交结果

image.png