这个题的关键在于去重,想要去重,最先想到的就是对数组或列表排序,排序后,依次以列表中的每个元素为起始值,查找后面的两个数之和等于这个起始值的负值,因为是排序后的,对于连续重复的元素,作为起始值可以直接跳过,因为再以它们为起点,也不会比第一个为起点,有更多地可能了。值得注意的是,后面两个值找到后,需要在夹逼的过程中,要注意跳过和当前值相同的元素。查找后面两个数的过程就是双指针向内逼近了。
自己的解法
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> resList = new ArrayList<>();
if (nums.length < 3) {
return resList;
}
// 排序,进行去重
Arrays.sort(nums);
for (int i = 0; i < nums.length - 2; i++) {
if (i > 0 && nums[i] == nums[i - 1]) {
continue;
}
int sum = - nums[i];
int left = i + 1;
int right = nums.length - 1;
while (left < right) {
if (nums[left] + nums[right] > sum) {
right--;
} else if (nums[left] + nums[right] < sum) {
left++;
} else {
List<Integer> res = Arrays.asList(nums[i], nums[left], nums[right]);
resList.add(res);
right--;
left++;
while (left < right && right < nums.length - 1 && nums[right] == nums[right + 1]) {
right--;
}
while (left < right && nums[left] == nums[left - 1]) {
left++;
}
}
}
}
return resList;
}
}
进阶解法:
思路和自己的解法类似,多了个nums[i]大于0的情况下,不用再找后面两个元素。还有就是命中了以后,进行去重的时候,先判断重复进行跳过,再进行常规跳过,这样可以少个右侧溢出的判断。
class Solution {
public static List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> ans = new ArrayList();
int len = nums.length;
if(nums == null || len < 3) return ans;
Arrays.sort(nums); // 排序
for (int i = 0; i < len ; i++) {
if(nums[i] > 0) break; // 如果当前数字大于0,则三数之和一定大于0,所以结束循环
if(i > 0 && nums[i] == nums[i-1]) continue; // 去重
int L = i+1;
int R = len-1;
while(L < R){
int sum = nums[i] + nums[L] + nums[R];
if(sum == 0){
ans.add(Arrays.asList(nums[i],nums[L],nums[R]));
while (L<R && nums[L] == nums[L+1]) L++; // 去重
while (L<R && nums[R] == nums[R-1]) R--; // 去重
L++;
R--;
}
else if (sum < 0) L++;
else if (sum > 0) R--;
}
}
return ans;
}
}