题目描述
给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != j、i != k 且 j != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请你返回所有和为 0 且不重复的三元组。
注 答案中不可以包含重复的三元组
题目解析
本题难点在于如何去重,笔者一开始想的是使用HashSet做结果候选集合的过滤,但是Set判断需要双重循环一个一个比对,配合最外层的结果候选集合就是三重for循环,最终会超过某些测试用例的时间限制。本题目首先确定一个数,用另外两个数的和等于 0 - 确定数 作为结果集筛选的判断
step1 首先需要对数组进行排序,这样才可以根据和的大小决定是左指针向右移动还是右指针向左移动
Arrays.sort(nums)
for(int i=0 ; i< nums.length -2; i++) {
int sum = 0 - nums[i];
int begin = i+1;
int end = nums.length-1;
while(begin < end) {
...
}
}
step 2 在双指针移动的过程中,如果nums[begin] + nums[end] 的和大于目标值,基于数组的有序性,我们应该向左移动右指针;如果是小于目标值,向右移动左指针;如果相等,记录此时的结果三元组,并且对于左右指针增加一个判断:
本题第一处去重逻辑应该放置在这里,在左右指针移动的情况下,排除重复元素,而不是在生成三元组结果集之后再去处理,减少计算的次数和循环的叠加,预先过滤掉元素重复的情况;如果 【-10 3 3 7 7 】这组数据不做过滤,会有重复的【-10 3 7】结果,导致本题不能通过。
数据过滤
如果左指针的下一个元素在区间内并且和左指针相等——跳过
如果右指针的前一个元素在区间内并且和右指针相等——跳过
因此可以给出这部分逻辑转换的Java代码:
if(nums[begin] + nums[end] < tmpSum) {
begin++;
}else if(nums[begin] + nums[end] > tmpSum){
end--;
}else{
tmpList.add(nums[begin]);
tmpList.add(nums[end]);
tmpList.add(nums[i]);
res.add(tmpList);
while (begin<end && nums[begin] == nums[begin+1]){
begin +=1;
}
while(begin < end && nums[end] == nums[end -1]) {
end -=1 ;
}
step3
如果不存在相等的情况,正常移动左右指针,相向移动一个元素:begin++ && end –
如果begin和end指针相遇的情况下,需要跳出while循环,移动最外层for循环中的标记 i,此时注意如果i的值和下一个值是相等的话,此时计算的三元组依然会有重复的情况,因此如果发生了i和i+1的值相等的情况下需要继续移动i跳过相等的情况
Arrays.sort(nums)
for(int i=0 ; i< nums.length -2; i++) {
if(i > 0 && nums[i] == nums[i-1]) continue;
int sum = 0 - nums[i];
int begin = i+1;
int end = nums.length-1;
while(begin < end) {
...
}
}
最终,附上完整版的代码,希望能够帮到有需要的读者们
完整题解
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> res = new ArrayList<>();
int count = 0;
Arrays.sort(nums);
for(int i=0; i< nums.length-2; i++){
if(i > 0 && nums[i] == nums[i-1]){
continue;
}
int j= i+1;
int tmpSum = count - nums[i];
int begin = j;
int end = nums.length -1;
List<Integer> tmpList = new ArrayList<>();
while(begin < end) {
if(nums[begin] + nums[end] < tmpSum) {
begin++;
}else if(nums[begin] + nums[end] > tmpSum){
end--;
}else{
tmpList.add(nums[begin]);
tmpList.add(nums[end]);
tmpList.add(nums[i]);
res.add(tmpList);
while (begin<end && nums[begin] == nums[begin+1]){
begin +=1;
}
while(begin < end && nums[end] == nums[end -1]) {
end -=1 ;
}
begin +=1;
end -= 1;
tmpList= new ArrayList<>();
}
}
}
return res;
}