题目
给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有和为 0 且不重复的三元组。
注意:答案中不可以包含重复的三元组。
示例 1:
输入: nums = [-1,0,1,2,-1,-4]
输出: [[-1,-1,2],[-1,0,1]]
来源:力扣(LeetCode)
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
代码
public class Leetcode15 {
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> result = new ArrayList<>();
// 1。 因为要保证每个结果集里面的元素不完全重复 先排序后在循环中比较与上一次是否相等,会比较简单
// 2。 排序之后还有一个特性,从小到大排了以后,当我们确定a的值以后,将b从小值往大值遍历时,如果每个b都有对应的c满足题目条件,那么随着b的增加,c
// 应该在一直减小;所以对于b的从小到大循环,c只需一个指针从大到小移动;这就是常用的双指针
// 当然,需要注意的时,b不一定有唯一的c对应,所以我们的判断条件时b+c>a,如果不满足,就将b指针+1,这样才可能继续满足这个条件
Arrays.sort(nums);
int len = nums.length;
for(int first = 0;first<len;first++){
if(first>0 && nums[first]==nums[first-1]){
// 首先保证不会完全重复
continue;
}
// 确定a,以后 可以确定目标值,即b+c
int target = -nums[first];
// c的起始遍历节点
int third = len-1;
for (int second = first+1;second<len;second++){
// 确定b以后 可以开始判断前面提到的逻辑
if(second>first+1 && nums[second]==nums[second-1]){
continue;
}
// 当还是满足b+c>target时,需要减小c,直到不满足
while (second<third && nums[second]+nums[third]>target){
third--;
}
// 此时两种情况:1. 知道b c重合都还是大于,就没有符合的条件了(此时已经最小了),可以跳出循环了
if(second==third){
break;
}
// 2. nums[second]+nums[third]==target,这个就是我们需要找的答案
if(nums[second]+nums[third]==target){
List<Integer> resultItem = new ArrayList<>(3);
resultItem.add(nums[first]);
resultItem.add(nums[second]);
resultItem.add(nums[third]);
result.add(resultItem);
}
// 第三种情况,nums[second]+nums[third]<target,这种情况就让c变大
}
}
return result;
}
}
解释
题目核心有两个要点:a+b+c=0 答案中不可以包含重复的三元组。
要解决这个题目,有两个重要的方法:
1.排序: 对于第二个条件,排序可以很好的解决,排序之后,当遍历第一层时,如果元素与上一个元素相等,则可以直接跳过(因为与上一个元素的逻辑数组重复)
2.双指针:对于上面的逻辑成立后,其实还是一个0(n3)的复杂度;所以还需要利用到一个特性,从小到大排了以后,当我们确定a的值以后,将b从小值往大值遍历时,如果每个b都有对应的c满足题目条件,那么随着b的增加,c应该在一直减小;所以对于b的从小到大循环,c只需一个指针从大到小移动;这就是常用的双指针;当然,需要注意的时,b不一定有唯一的c对应,所以我们的判断条件时b+c>a,如果不满足,就将b指针+1,这样才可能继续满足这个条件
总结
又是一道双指针经典题目,对于双指针题目;没想到用双指针时,做起来会很吃力;题目遇多+总结后,再做就会思路很顺畅;
引用leetcode里面的一句总结 “ 这个方法就是我们常说的「双指针」,当我们需要枚举数组中的两个元素时,如果我们发现随着第一个元素的递增,第二个元素是递减的,那么就可以使用双指针的方法 ”
其实这个题目也是这样,我们首先第一层循环确定a以后;剩下的就是在剩余的有序数组中中确定b和c,可以减少一层复杂度