每日一题 - 三数之和

78 阅读2分钟

点赞再看,养成习惯


题目

1. 描述

给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != j、i != k 且 j != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请你返回所有和为 0 且不重复的三元组。注意:答案中不可以包含重复的三元组。

来源:力扣(LeetCode)

链接:leetcode.cn/problems/3s…

示例1:

输入:nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2],[-1,0,1]]
解释:
nums[0] + nums[1] + nums[2] = (-1) + 0 + 1 = 0 。
nums[1] + nums[2] + nums[4] = 0 + 1 + (-1) = 0 。
nums[0] + nums[3] + nums[4] = (-1) + 2 + (-1) = 0 。
不同的三元组是 [-1,0,1][-1,-1,2] 。
注意,输出的顺序和三元组的顺序并不重要。

示例2:

输入: nums = [0,1,1]
输出: []
解释: 唯一可能的三元组和不为 0 。

题解

1. 双指针

思路:

  1. 我们首先可以将数组进行排序,排序后的数组方便我们按照大小逻辑进行计算
  2. 传统我们暴力破解就是通过三次循环(时间复杂度O(n^3))进行操作,但是此题我们在力扣测试若使用暴力破解会提示超时,因此我们需要想一个办法优化我们的算法。
  3. 我们可以观察规律,让我们假设三个数分别为A,B,C,由于我们是一个有序数组,则可以得到一个结论:A<B<C,这个结论是绝对成立的。
  4. 根据上述结论我们可以进一步推导若A>0 ,则A+B+C 必定大于0
  5. A<=0 则可以推出0- A =B+C
  6. 此时如我们第一次遍历数组for (int i = 0; i < nums.length - 2 && nums[i] < 1; i++),此时A = nums[i]为固定数 ,因此我们只需要寻找剩余的B+C = 0-A 即可。
  7. 阶段性结论: 刚刚我们推导出A < B < CB+C = 0-A ,此时A为固定数,因此我们只需要通过指针在数组中移动查询B+C = -A 即可。 传统的方式我们采用两层循环,在数组从左到右进行遍历判断,但是这种方式会超出题目要求的运行时间,因此我们需要对这种方式进行优化。
  8. 重要结论推导: 此时我们假定存在两个指针分别为leftIndexrightIndex ,此时有-> nums[leftIndex] = B,nums[rightIndex] = C ,由于B<C,且数组有序,因此得到leftIndex <rightIndex
  9. 因为leftIndex <rightIndex , 因此我们可以控制指针leftIndex 从左到右,rightIndex从右到左进行遍历,此时我们仅需遍历第二次数组即可,无需第三次遍历。

代码:

/*双指针*/
private static List<List<Integer>> threeSum(int[] nums) {
    ArrayList<List<Integer>> lists = new ArrayList<>();
    /*去重*/
    Map<String, Boolean> duplicate = new HashMap<>();

    /*第一步 排序*/
    Arrays.sort(nums);
    for (int i = 0; i < nums.length - 2 && nums[i] < 1; i++) {
        Integer leftIndex = i + 1;
        Integer rightIndex = nums.length - 1;

        while (leftIndex < rightIndex) {
            if (nums[i] + nums[rightIndex] + nums[leftIndex] == 0 && duplicate.get("" + nums[i] + nums[rightIndex] + nums[leftIndex]) == null) {
                ArrayList<Integer> integers = new ArrayList<>();
                integers.add(nums[i]);
                integers.add(nums[leftIndex]);
                integers.add(nums[rightIndex]);
                lists.add(integers);
                duplicate.put("" + nums[i] + nums[rightIndex] + nums[leftIndex], true);
                leftIndex++;
                rightIndex--;
                continue;
            } else if (nums[i] + nums[rightIndex] + nums[leftIndex] > 0) {
                rightIndex--;
                continue;
            } else {
                leftIndex++;
            }
        }
    }
    return lists;
}

复杂度分析:

  • 时间复杂度:O(N^2)。
  • 空间复杂度:O(N)。

运行结果:

image.png

结语

今天的内容就到此结束了,有疑问的小伙伴欢迎评论区留言或者私信博主,博主会在第一时间为你解答。

码字不易,感到有收获的小伙伴记得要关注博主一键三连,不要当白嫖怪哦~

如果大家有什么意见和建议请评论区留言或私聊博主,博主会第一时间反馈的哦