三数之和
题目:
参数列表:给定一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?返回值找出所有满足条件且不重复的三元组。
补充知识;
三元组实例化方式
List<List<Integer>> ans=new ArrayList<>();
//List是一个接口,可以使用ArrayList或LinkedList来实现
三元组添加元素的方式
//第一种方式:
List<Integer> list=new ArrayList();
list.add(nums[i]);
list.add(nums[l]);
list.add(nums[r]);
ans.add(list);
//第二种方式,会比第一种方式耗时短,
//其中Arrays.asList(int,int ~)
//> static <T> List<T> asList(T... a)
//返回由指定数组支持的固定大小的列表。
ans.add(Arrays.asList(int,int,~))
思路一:
三个for循环。时间复杂度log(n^3)
思路二:
我们由两数之和可以想到运用两个指针。
或者
另一种办法是:数组里的元素存在一个set里,遍历数组,求出partial_target=target-nums[i],然后再set里面寻找partial_target。
但是这样容易会有错误。例如:数组中含有一个12,target=24,p_t=12,在set里面找p_t是可以找到的,但是[[12,12]]并不是我们要的答案,因为12只能被使用一次。
于是我们改进,将数组里的元素不再存入set,而是存入hashset,一并存入的还有元素出现的次数,如果出现的次数大于1,那么该元素可以被使用第二次。
如何在三数之和中使用双指针。
1.sort,排序。
2.从第一个元素 nums[0] 开始,对后面的元素进行查找 --两数之和为 temp= - nums[0] 的元素
(1)左指针 l=1,右指针 r=nums.length-1
(2)如果 l<r,则重复下面动作
nums[l]+nums[r] 与 temp进行比较
如果大于,则右指针左移
如果小于,则左指针右移
否则,返回三元组[[i,l,r]]
3.重复以上步骤,直到i>nums.length-2
细节优化:
第二步中,判断遍历到的元素是否大于0,如果是,因为数组已经被排序过,则后面的也肯定大于0,所以没有必要继续向下比较。
第二步中,nums[l]+nums[r] ==temp后,我们直接将 i l r 存入三元组,有可能会出现重复,例如测试案例{0,0,0,0} 。两种解决方法:
(1)利用contains()判断 ans 中是否包含有我们即将存入的三元组,如果没有,则存入
if(!ans.contains(Arrays.asList(nums[i],nums[l],nums[r])))
{
ans.add(Arrays.asList(nums[i],nums[l],nums[r]));
}
但是由于contains()还要遍历一次List,所以时间复杂度高。
(2)我们在遍历的时候就刻意跳过重复的元素
三元组第一个元素跳过重复元素
if(i>0&&nums[i]==nums[i-1])
continue;
注意:这边不可以使用以下代码,测试案例{0,0,0,0,}
while(i>0&&nums[i]==nums[i-1])
i++;
三元组后面两个元素跳过重复元素
while (l<r && nums[l] == nums[l+1]) l++;
while (l<r && nums[r] == nums[r-1]) r--;
完整的代码
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> ans=new ArrayList<>();
Arrays.sort(nums);
int l,r;
for (int i = 0; i < nums.length-2; i++) {
if(nums[i]<=0)
{
if(i>0&&nums[i]==nums[i-1])
continue;
l=i+1;
r=nums.length-1;
int temp=-(nums[i]);
while(l<r)
{
if(nums[l]+nums[r]<temp)
l++;
else if(nums[l]+nums[r]>temp)
r--;
else if(nums[l]+nums[r]==temp)
{
List<Integer> list=new ArrayList();
list.add(nums[i]);
list.add(nums[l]);
list.add(nums[r]);
ans.add(list);
//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
break;
}
return ans;
}