代码重构: leetcode 15. 三数之和

153 阅读1分钟

15. 三数之和

我们知道这题思路是固定一个数字,然后按两数之和来求,但有一点需要注意的是如何求去重的。

显然,如果想去重,可以直接把答案放入set,然后写compare函数就可以解决,但这样太慢,有没有可能在遍历过程中去掉所有重复呢?

答案是:有的。

假定我们的答案三元组<a,b,c>是非递减的。先将数组排序,于是我们采取以下策略就可以完成非重复选举。

  1. 循环先确定a,然后再确定b和c,a不能与前一次确定的相同。
  2. 再确定b和c,在每一次确定完b和c后,将b往后移动到与这次确定的b不同的位置再继续。

下面证明为何这样选择出来的一定是不同的答案。

显然因为<a,b,c>是非递减的,又因为每次选举的a是不会重复的,于是每次对于a的结果都是不同的,而第二步,确定b和c的时候,如果确定了一次,就将b移动到之后不同的位置,又因为是有序的,于是也不会出现两个相同的b,于是就证明完成,不会出现两个相同的a,在同一a下,不会出现两个相同的满足条件的b,于是得到的都是不同的三元组。

    public  List<List<Integer>> threeSum(int[] nums) {
        List<List<Integer>> ans = new ArrayList<>();
        Arrays.sort(nums);
        for (int i = 0; i <= nums.length - 3; i++) {
            if (i > 0 && nums[i] == nums[i - 1]) continue;
            int left = i + 1, right = nums.length - 1, target = -nums[i];
            while (left < right) {
                if (nums[left] + nums[right] < target) left++;
                else if (nums[left] + nums[right] > target) right--;
                else {
                    List<Integer> temp = new ArrayList<>();
                    temp.add(nums[i]);
                    temp.add(nums[left]);
                    temp.add(nums[right]);
                    ans.add(temp);
                    left++;
                    while (left+1<nums.length&&nums[left] == nums[left-1]) left++;
                }
            }
        }
        return ans;
    }