题目描述
给定一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?找出所有
满足条件且不重复的三元组。
注意:答案中不可以包含重复的三元组。
例如, 给定数组 nums = [-1, 0, 1, 2, -1, -4],
满足要求的三元组集合为:
[
[-1, 0, 1],
[-1, -1, 2]
]
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/3sum
思路:首先将数组从小到大排序。假设三个数为L,M,R。M从1到length-1。L为最左边的数,R为最右边的数,M为中间的数。如果L+M+R>0则证明R偏大,R–。否则为L偏小,L++。如果三者相加等于0,则存入List。直到LM||RM。
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> ans = new ArrayList<>();
Arrays.sort(nums); // 排序
int len = nums.length;
if (len < 3) return ans;//没有答案
for (int i = 1; i < len - 1; i++) {
int L = 0;
int R = len - 1;
while (L < i && R > i) {
int sum = nums[L] + nums[i] + nums[R];
if (sum == 0) {
ans.add(Arrays.asList(nums[i], nums[L], nums[R]));
L++;
R--;
} else if (sum < 0) L++;//L偏小
else R--;//R偏大
}
}
return ans;
}
}
写完了,美滋滋地贴上去,结果发现。
原来是有重复的值未处理啊。emm…这可如何是好。
既然有重复的,那就试图把它去了呗。
1.如果M的前一个值和当前相等,跳过当前值
2.如果L的后一个值和当前相等,跳过当前值
3.如果R的后一个值和当前相等,跳过当前值
好的,代码来了:
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> ans = new ArrayList<>();
Arrays.sort(nums); // 排序
int len = nums.length;
if (len < 3) return ans;
for (int i = 1; i < len - 1; i++) {
if(nums[i-1]>0) break;//第一个数大于0,后面肯定不会有答案
if (i > 1 && nums[i - 1] == nums[i]) continue;//去重
int L = 0;
int R = len - 1;
while (L < i && R > i) {
int sum = nums[L] + nums[i] + 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 R--;
}
}
return ans;
}
}
快快乐乐的提交,本以为完全没问题了,可是结果。
分析用例,发现{-4,-1,-1,0,1,2},当-1和-1想当时,M会跳过后一个-1。然而L用这个-1,正好可以组成一组解。M每后移一次,L与R的取值返回一定会变,所以不能单纯的判定M相等就跳过M,因为M会决定L与R的取值,跳过M后,可能会错过某些答案。而L与R再怎么移动也不会改变其他两个变量的取值。所以L与R可以跳过。那么有什么办法可以安全地跳过M呢。
在跳过之前再加个判断,如果M的值已经有了一组解,那么这个 M就可以跳过了(因为已经有了解,所以没有必要在求解了)。否则,不可以跳过(没有解,并且L与R的取值范围已经变化,所以可能会有解)。
这样就需要再些一个判断是否已经有解的方法,遍历ans List。
看了题解,发现了一个更好的方法:
首先对数组进行排序,排序后固定一个数nums[i],再使用左右指针指向nums[i]后面的两端,数字分别为nums[L]和nums[R],计算三个数的和sum判断是否满足为 0,满足则添加进结果集
如果nums[i]大于 0,则三数之和必然无法等于 0,结束循环
如果nums[i] = nums[i-1],则说明该数字重复,会导致结果重复,所以应该跳过
当sum = 00 时,nums[L] = nums[L+1]则会导致结果重复,应该跳过,L++
当sum =0时,nums[R] = nums[R-1]则会导致结果重复,应该跳过,R–
class Solution {
public static List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> ans = new ArrayList();
Arrays.sort(nums); // 排序
int len = nums.length;
if(nums == null || len < 3) return ans;
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;
}
}
作者:guanpengchn
链接:https://leetcode-cn.com/problems/two-sum/solution/hua-jie-suan-fa-15-san-shu-zhi-he-by-guanpengchn/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
将M放在头位,L放在M后一位,R放在最右。这样无论M如果跳过都不会影响结果,因为跳过的那个值,L在上一轮已经求解过了。