给定一个包含 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) {
}
}
这道题目很有意思,这里提供两种方法:
首先第一种是蛮力法,也是我最先想到的,嗯
这个方法的优点是思维简单,缺点是时间复杂度高,当输入数据较多时,难以获得有效的满意度。
蛮力法的思路是三次循环计算每三个组合,然后进行计算满足的三个数,和已在表中的进行对比进行去重,依次做下去。
代码如下:
public static List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> a=new ArrayList<>();
int len=nums.length;
for(int i=0;i<len-2;i++){
for(int j=i+1;j<len-1;j++)
for(int k=j+1;k<len;k++){
if(nums[i]+nums[j]+nums[k]==0){
List<Integer> temp=new ArrayList<>();
temp.add(nums[i]);
temp.add(nums[j]);
temp.add(nums[k]);
temp.sort(null);//有序的才能进行比较
if(!a.contains(temp)){
a.add(temp);
}
}
}
}
return a;
}
第二种是双指针法,我看了下别人的题解,用自己的语言给大家整理下
我们为了减轻复杂度,要对数组进行排序
然后呢我们对数组进行遍历,从小到大依次为最小值,当最小值都大于0时,必三数之和大于0,所以我们某种意义上就排除了一半的时间复杂度。
接下来就是循环过程中,右指针是最大值开始,左指针为最小值的前一个开始。当右指针小于等于左指针时我们要进行下一轮循环,同时在本轮循环中,当三数和大于0时,我们将右指针向左移,也就是三数之和变小
当三数之和小于0时,我们将左指针向右移,三数之和也就变大了。
我们减轻复杂度的方法,比蛮力法少了列表去重,因为我们将数组排好序后,重复的元素通过指针就自动去重了。
代码如下:
public static List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> a=new ArrayList<>();
int len=nums.length;
Arrays.sort(nums);//数组排序
if(len<3||nums==null)
return a;//极限条件,数组为空或长度小于3时
else if(nums[0]<=0&&nums[len-1]>=0){//当最小都大于0,或是最大都小于0,也就无三数之和==0的可能性了。
for(int i=0;i<len;i++){//开始循环
if(nums[i]>0) break;//最小值大于0时跳出循环
if(i>0&&nums[i]==nums[i-1]){
continue;//去除最小值的重复元素
}//确定左右指针位置
int Left=i+1;
int Right=len-1;
while(Left<Right){
int sum=nums[i]+nums[Left]+nums[Right];
if(sum==0){
a.add(Arrays.asList(nums[i],nums[Left],nums[Right]));
while(Left<Right&&nums[Left]==nums[Left+1]) Left++;//去左重
while(Left<Right&&nums[Right]==nums[Right-1]) Right--;//去右重
Left++;//指针变化
Right--;
}
else if(sum<0)
Left++;//和小于0左指针右移,和变大
else if(sum>0)
Right--;//和大于0 右指针左移,和变小
}
}
}
return a;
}
最后宣传下我个人的微信公众号,微信搜索:可及的小屋,有志向整副业,娱乐的程序员们,欢迎您的到来。谢谢。
100G程序员资料,自取哦!!