题目:给出一个有n个元素的数组S,S中是否有元素a,b,c满足a+b+c=0?找出数组S中所有满足条件的三元组。
注意:
- 三元组(a、b、c)中的元素必须按非降序排列。(即a≤b≤c)
- 解集中不能包含重复的三元组。
这道题,我是想着直接用暴力求解,三重循环。。。结果答案对比是一样的,这个测试就是不给我通过,就很奇怪,然后就去看了其他人的题解,使用的是双指针,整体思想是把三数和为0转换为两数和为0 ,还是有几个需要注意的点的。
首先为了方便处理,先将数组进行排序,(以后类似的题目都要注意,对顺序没有要求的,并且是乱序的,先去考虑一下对数组排序是不是能够简化处理),将数组排序之后就使得我们找三个和为0的数字有规律可循,并且能够简化很大部分的比较。
首先使用循环对第 0 - num.length-3的元素进行遍历,通过确定当前元素,将三数之和转换为了两数之和,在每次进行处理之前需要进行一步去重操作,由于解集是要求不包含重复三元组的,因此如果当前元素等于其前一个元素的话就直接跳过不用处理,对于每个需要处理的元素 i ,首先确定 left = i+1,right = num.length-1,然后对num[i] , num[left] , num[right] 三数之和进行判断,
1)当 left<right 并且三数之和大于0时,说明 right 需要左移,因为数组是升序排序的,右移变大,左移变小,所以需要 right 左移来减小三数之和。
2)当 left<rigjt 并且三数之和等于0时,好的,找到一组解了,将三个数 add 进 list 中,然后 sort 一下,然后添加到最终结果的 list 中。做完这一步需要注意,由于left和right的遍历还在继续,为了避免重复的三元组,我们需要将 left 右边的等于 left 的元素跳过,left++进行右移。
3)left<right 并且三数之和小于0的话,那么 left 右移移来增大三数之和。
完整代码如下:
public ArrayList<ArrayList<Integer>> threeSum(int[] num) {
ArrayList<ArrayList<Integer>> res = new ArrayList<>();
Arrays.sort(num);
for(int i=0;i<=num.length-3;i++) {
if(i==0||num[i]!=num[i-1]) {//跳过重复数字
int left = i+1;
int right = num.length-1;
while(left<right) {
while (left<right&&((num[i]+num[left]+num[right])>0)) { //大于0的话让right变小
right--;
}
if(left<right&&num[i]+num[left]+num[right]==0) {
ArrayList<Integer> temp = new ArrayList<Integer>();
temp.add(num[i]);
temp.add(num[left]);
temp.add(num[right]);
Collections.sort(temp);
res.add(temp);
int templeft = num[left];
while(left<right&&num[left]==templeft) {
left++;
}
}else {
left++;
}
}
}
}
return res;
}