codeTop100题(6)15. 三数之和

69 阅读2分钟

1. 题目

15. 三数之和

2. 分析

题目有两个要求:

  • i!=j!=k
  • 三元组不能重复

暴力的解法是连续三次遍历,为了达成第一个条件,我们需要遍历的时候控制i < j < k;为了达成第二个条件,我们需要当满足条件的ijk进行跳过重复的数,这就需要我们对数组进行排序。

3. 代码

3.1 暴力解法

class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        //先排序
        Arrays.sort(nums);

        List<List<Integer>> lists = new ArrayList<>();
        for (int i = 0; i < nums.length - 2; i = skip(nums, i)) {
            for (int j = i + 1; j < nums.length - 1; j = skip(nums, j)) {
                for (int k = j + 1; k < nums.length; k = skip(nums, k)) {
                    if (nums[i] + nums[j] + nums[k] == 0) {
                        lists.add(Arrays.asList(nums[i], nums[j], nums[k]));
                    }
                }
            }
        }
        return lists;
    }
    // 跳过重复的数
    public int skip(int[] nums, int i) {
        do {
            i++;
        } while (i < nums.length && nums[i - 1] == nums[i]);
        
        return i;
    }
}

时间复杂度o(n^3),提交的结果如下:

image.png

3.2 优化

暴力的方法显然没办法解决所有的case,当数据量大了之后o(n^3)的复杂度耗时会很久,那么是否有一个能够o(n^2)的方案呢?要从n^3变成n^2,即将n^2 变成 n,我们可以考虑使用双指针的方法:

  1. 排序后,固定一个i从0开始遍历,当nums[i]>0时,中断流程
  2. 将l r 双指针分别指向i+1和nums.length - 1
  3. 记录s 为 nums[i] + nums[l] + nums[r]
  • 当 s > 0,r左移
  • 当 s < 0, l右移
  • 当 s = 0, 记录三元组,且跳过所有的重复数

这样子我们的时间复杂度就变成n^2了

class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        Arrays.sort(nums);
        List<List<Integer>> lists = new ArrayList<>();
        for (int i = 0; i < nums.length - 2; i++) {
            if (nums[i] > 0) {
                break;
            }
            if (i > 0 && nums[i - 1] == nums[i]) {
                continue;
            }
            int l = i + 1;
            int r = nums.length - 1;
            while (l < r) {
                int s = nums[i] + nums[l] + nums[r];
                if (s > 0) {
                    r--;
                } else if (s < 0) {
                    l++;
                } else {
                    lists.add(Arrays.asList(nums[i], nums[l], nums[r]));
                    int temp = nums[r];
                    do {
                        r--;
                    } while (r > l && nums[r] == temp);
                    temp = nums[l];
                    do {
                        l++;
                    } while (r > l && nums[l] == temp);
                }
            }
        }
        return lists;
    }

}

4. 总结

这题最开始没有想到可以使用双指针来将n^2的时间复杂度变成n。记忆一下,后续做这种题目的时候可以考虑一下使用双指针