很有意思的问题,也让我重新看了一下第一题。
原题:
Given an array S of n integers, are there elements a, b, c in S such that a + b + c = 0? Find all unique triplets in the array which gives the sum of zero.
Note: The solution set must not contain duplicate triplets.
For example, given array S = [-1, 0, 1, 2, -1, -4],
A solution set is:
[
[-1, 0, 1],
[-1, -1, 2]
]
解答如下,要点就是转化成2Sum问题,然后剩下两个数从头尾开始碰到中间。
public static List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> v = new ArrayList<>();
if (nums.length < 3) {
return v;
}
// 1. 暴力解法,显然是错的
// for (int i = 0; i < nums.length ; i++) {
// int sum = -nums[i];
// HashSet<Integer> set = new HashSet<>();
// for (int j = i + 1; j < nums.length; j++) {
// if (set.contains(sum - nums[j])) {
// List<Integer> list = new ArrayList<>();
// list.add(nums[i]);
// list.add(nums[j]);
// list.add(sum - nums[j]);
// Collections.sort(list);
// if (!v.contains(list)) {
// v.add(list);
// }
// }
// else{
// set.add(nums[j]);
// }
// }
// }
// 2. 重点在于,之前的twoSum在O(n^2)的复杂度上执行,其实只要明白a < b的特点,然后让a和b在数组两端收缩就可以了
Arrays.sort(nums); // 需要先排序,才能去重
for (int i = 0; i < nums.length - 2 ; i++) {
if (i == 0 || nums[i] != nums[i-1]) {
// 不重复取第一个数字
int low = i + 1;
int high = nums.length - 1;
while(true){
if (low >= high) {
break;
}
else if (nums[low] + nums[high] > - nums[i]) {
// high的太大了,要让右边的小一些
high--;
}
else if (nums[low] + nums[high] < - nums[i]) {
// low的太小了,要让左边的大一些
low++;
}
else{
v.add(Arrays.asList(nums[low], nums[high], nums[i]));
int highnum = nums[high];
while(high >= 0 && nums[high] == highnum)high--;
// 去掉重复的high
int lownum = nums[low];
while(low < nums.length && nums[low] == lownum)low++;
// 去掉重复的low
}
}
}
}
return v;
}