大体思路:a + b + c = 0 转化为 a + b = -c
方法二:外层for确定-c,内层用双指针替代两个for. 排序判断去重
考虑例子:
因此:
-
给数组排序,枚举的三元组 (a, b, c) 满足大小顺序,保证了只有(a,b,c) 这个顺序会被枚举到,而 (b,a,c)、(c,b,a)不会
-
之后对
nums[i] nums[j] nums[k]都要去重
//方法二 排序+双指针,用双指针代替了两个for
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
Arrays.sort(nums); // 先排序!!!
List<List<Integer>> res = new ArrayList<>();
for (int i = 0; i < nums.length; i++) {
if (nums[i] > 0) {//如果基准数 > 0,直接返回结果,后面的不用看了
return res;
}
if (i > 0 && nums[i] == nums[i - 1] ) {//如果基准数重复
continue;
}
int j = i + 1;//j为nums[i+1,end]的首
int k = nums.length - 1;//k为尾
while (j < k) {
// 【把常出现的情况放前面 优化速度】
if (nums[i] + nums[j] + nums[k] < 0) {//sum小于0,期待更大的sum,左指针右移
j++;
} else if (nums[i] + nums[j] + nums[k] > 0) {//sum大于0,期待更小的sum,右指针左移
k--;
} else { // nums[i] + nums[j] + nums[k] == 0
List<Integer> tmp = new ArrayList<>();
tmp.add(nums[i]);
tmp.add(nums[j]);
tmp.add(nums[k]);
res.add(tmp);
//找到一例三元组后,判断左右指针是否重复
//如果左指针对应的数重复,注意防止00000的情况越界
while (j + 1 < nums.length && nums[j] == nums[j + 1]) {
j++;
}
//如果右指针对应的数重复,注意防止00000的情况越界
while (k - 1 >=0 && nums[k] == nums[k - 1]) {
k--;
}
//收缩区间
j++;
k--;
}
}
}
return res;
}
}
核心代码
for (i遍历) {
//i判断重复
while (left < right) {
//双指针left,right分情况移动并判断重复
}
}
方法一: 外层for循环确定-c,内层两数之和,set去重
//方法一
/**
* a + b + c = 0 转化为 a + b = -c
* 利用two sum的HashMap做法
* 利用set去重
*/
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> res = new ArrayList<>();
Set<List<Integer>> tmpres = new HashSet<>();
for (int i = 0; i < nums.length; i++) {//外层循环遍历数组,确定新的目标值-c
Set<Integer> set = new HashSet<>();//Set声明的位置要注意,应该是确定新的目标值之后
for (int j = i + 1; j < nums.length; j++) {//内层循环,两数之和
if (set.contains(-nums[i] - nums[j])) {
List<Integer> list = new ArrayList<>();
list.add(nums[i]);
list.add(-nums[i]- nums[j]);
list.add(nums[j]);
Collections.sort(list);//对某一符合条件的三元组排好序
tmpres.add(list);//加到结果set中,对于重复的元素,set不会再次添加
} else {
set.add(nums[j]);
}
}
}
return new ArrayList<>(tmpres);//set转为list
}
}