15. 三数之和
我们知道这题思路是固定一个数字,然后按两数之和来求,但有一点需要注意的是如何求去重的。
显然,如果想去重,可以直接把答案放入set,然后写compare函数就可以解决,但这样太慢,有没有可能在遍历过程中去掉所有重复呢?
答案是:有的。
假定我们的答案三元组<a,b,c>是非递减的。先将数组排序,于是我们采取以下策略就可以完成非重复选举。
- 循环先确定a,然后再确定b和c,a不能与前一次确定的相同。
- 再确定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;
}