题目链接
给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有满足条件且不重复的三元组。
注意: 答案中不可以包含重复的三元组。
示例:
给定数组 nums = [-1, 0, 1, 2, -1, -4],
满足要求的三元组集合为: [ [-1, 0, 1], [-1, -1, 2] ]
本题有两种解法,一般推荐使用第一种双指针法,还有另一种比较麻烦的方法哈希表法,主要难点在于去重,本文主要解释第二种哈希表法,双指针法请见其他文章。
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
//哈希表法
List<List<Integer>> res=new ArrayList<>();
Arrays.sort(nums);
for (int i = 0; i < nums.length-2; i++) {
if(nums[i]>0){
return res;
}
if(i>0&&nums[i]==nums[i-1]){
continue; //第一轮相同的已经全部找过了,再重复找也是子集
}
Set<Integer> set=new HashSet<>(); //保存c,放外面的话会保存非neg值(不是-b-c)导致错误
for (int j = i+1; j < nums.length; j++) {
if(j>i+2&&nums[j]==nums[j-1]&&nums[j-1]==nums[j-2]){
continue; //可以允许一个是一样的因为c可能是前一个相同的,
// 但是第三个再相同就重复了,因为b已经在哈希表里了成功的话重复
// 不成功的话也没必要再判断一次
}
int neg=-nums[i]-nums[j];
if(set.contains(neg)){
res.add(Arrays.asList(nums[i],nums[j],neg));
set.remove(neg); //防止ab顺序不一样但是数字一样的情况
}else{
set.add(nums[j]);
}
}
}
return res;
//双指针法
// List<List<Integer>> res=new ArrayList<>();
// Arrays.sort(nums);
// for (int i = 0; i < nums.length-2; i++) {
// //排序后如果第一个元素就大于0那么就不可能符合了
// if(nums[i]>0){
// return res;
// }
// //对a去重
// if(i>0&&nums[i]==nums[i-1]){
// continue;
// }
// int l=i+1;
// int r= nums.length-1;
// while(r>l){ //找三个元素所以不能重复用>
// int sum=nums[i]+nums[l]+nums[r];
// if(sum>0){
// r--;
// }else if(sum<0){
// l++;
// }else {
// res.add(Arrays.asList(nums[i],nums[l],nums[r]));
// while(r>l &&nums[r]==nums[r-1]){
// r--;
// }
// while(r>l && nums[l]==nums[l+1]){
// l++;
// }
// r--;
// l++;
// }
// }
// }
// return res;
}
}
主要思想:利用哈希表记录下可能的c值,即为-a-b的值,每次固定遍历下a+b的值使用哈希表记录相应的c值,所以哈希表存在于第一个循环内,第二个循环外,可以在a值变换之后自动清空哈希表中的内容,防止下一轮的哈希表仍然存在上一轮的-a-b的值,和这一轮的c值相加并不等于0导致产生错误。
去重逻辑:a去重解释,第一轮a已经将所有可能的组合全部找过了,下一轮再重复找相等的a也是上一轮组合的子集导致重复,所以a重复直接跳过;b去重解释,可以允许b和之前重复一次,因为c可能是前一个相同的b值,这也是一种三元组可能,直接跳过可能会漏掉一种组合,但是第三个再相同不跳过就会重复了,因为上一轮b已经记录在哈希里了成功的话会查到重复组合,不成功的话也没必要再白白判断一次;c去重解释,防止a值和b值相等但是顺序不相等的可能,如果不去重产生a、b、c和b、a、c的话会导致结果重复,所以c用完之后就需要清除。