leetcode 力扣 15 三数之和

67 阅读2分钟

算法思路

三指针i, j, k,在每一轮遍历中,固定ijk向中间移动寻找和为0

所以问题的关键是如何移动这三个指针,避免重复的数组。

对于i,j, k,我们需要与它们上一位已经遍历过的数比较

  • nums[i - 1]nums[i]比较,如果相等,i++
  • j - 1j比较,如果相等,j++
  • kk + 1比较,如果相等,k--

是不是比较反直觉?为什么不跟下一位比较呢?也就是为什么不能ii + 1比较,jj + 1比较,k - 1k比较?

因为这样会错过正确数字

比如下图,i直接越过了上一个-1,这样就错过了[-1, -1, 2]

while (i < n - 2 && nums[i] == nums[i + 1]) {
    i++;
}

1.jpeg

比如下图,在找出[-2, 0, 2]后,j也是直接越过了上一个1,而k直接执行k--,导致退出while,错过[-2, 1, 1]

res.add(Arrays.asList(x, nums[j], nums[k]));

do {
    j++;
} while (j < k && nums[j] == nums[j + 1]);

do {
    k--;
} while (j < k && nums[k] == nums[k - 1]);             

2.jpeg

所以说,要不停与过去的自己比较,不要急着往前跑,才不会错过将来

class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        List<List<Integer>> res = new ArrayList<>();
        int n = nums.length;

        // 排序,使用重复的数相邻
        Arrays.sort(nums);

        for (int i = 0; i < n - 2; i++) {

            // 如果第一个数都大于0,后两个数也是大于0
            if (nums[i] > 0) {
                return res;
            }

            while (0 < i && i < n - 2 && nums[i - 1] == nums[i]) {
                i++;
            }

            int x = nums[i];
            int j = i + 1;
            int k = n - 1;

            while (j < k) {
                int sum = x + nums[j] + nums[k];
                if (sum == 0) {
                    res.add(Arrays.asList(x, nums[j], nums[k]));

                    // 先向右移动指针,再判断是否重复
                    do {
                        j++;
                    } while (j < k && nums[j - 1] == nums[j]);

                    do {
                        k--;
                    } while (j < k && nums[k] == nums[k + 1]);
                } else if (sum < 0) {

                    // 太小了,往大的移动
                    j++;
                } else {

                    // 太大了,往小的移动
                    k--;
                }
            }
        }

        return res;
    }
}