给你一个包含 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]]
示例 2:
输入:nums = []
输出:[]
示例 3:
输入:nums = [0]
输出:[]
提示:
0 <= nums.length <= 3000
-105 <= nums[i] <= 105
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/3sum
如果题目中没有要求重复的话,那么我们可以利用三层循环即可实现,但是这里要求了最后的结果是没有重复的三元组,所以应该怎么办呢?答案很简单,只要将数组进行排序,那么获取到的三元组就是一个已经排好序的了,这时候我们就能够轻易的判断是否重复。但是题目给出的数组长度较大,那么就会容易导致超时,是否可以对算法进行优化呢?当然是可以的,因为数组经过了排序,所以当当前这个数字和前一个数字相等的时候,即例如nums[ i ] = nums[ i - 1],那么由于nums[ i - 1]已经将所有的情况都已经枚举过了,所以nums[ i ]就不需要再次进行枚举了。请看下面的例子:
尽管是这样做了,但是最后提交的时候还是错误的,由于是三层循环,那么导致可能超时,所以需要再一次进行优化,那么怎么进行优化呢?这就是利用到了双指针的思想。
没有进行优化时的代码:
for(i = 0; i < nums.length - 2; i++){
if(i == 0 || nums[i] != nums[i - 1]){
for(j = i + 1; j < nums.length - 1; j++){
if(j == i + 1 || nums[j] != nums[j - 1]){
for(k = j + 1; k < nums.length; k++){
if((k == j + 1 || nums[k] != nums[k - 1] )&& nums[i] + nums[j] + nums[k] == 0){
subList = new ArrayList<Integer>();
subList.add(nums[i]);
subList.add(nums[j]);
subList.add(nums[k]);
list.add(subList);
}
}
}
}
}
}
第三层的循环这里进行修改,不再是从第二层循环j的下一个元素开始,而是第三层循环都是从最后一个元素开始,然后计算nums[ i ] + nums[ j ] + nums[ k ]是否大于0,如果是,那么k–,一直重复这个操作,直到nums[ i ] + nums[ j ] + nums[ k ] <= 0,此时判断他们的和是否等于0,如果等于,那么将这三个数字添加到集合中,作为其中一个三元组,然后j后移,否则,如果小于,j同样后移,因为这样才有可能使得这三个数的和等于0.
优化后的代码:
for(i = 0; i < nums.length - 2; i++){
if(i == 0 || nums[i] != nums[i - 1]){
k = nums.length - 1;//每次k都是从最后一个元素开始的
for(j = i + 1; j < nums.length - 1 && k > j; j++){
if(j == i + 1 || nums[j] != nums[j - 1]){
/*
由于要求不重复,那么第三个元素必然是再第二个元素的右边,即必
须满足k > j,否则就会导致出现重复的情况
*/
while(k > j && nums[i] + nums[j] + nums[k] > 0)
k--;
/*
如果nums[i] + nums[j] + nums[k] < 0的时候,并不是
直接退出循环,而是需要将j后移一个位置,因为有可能
出现类似于[-3,-1,1,2]的情况,此时i = 0,即对应值
为-3,j = 1,即对应值为-1,此时k=3,这时候三者的和小
于0,所以k依旧是3,这时候不是退出循环,而是将j后移
使得j = 2,对应的值为1,此时三数的和为0,三元组
为[-3,1,2]
*/
if(k > j && nums[i] + nums[j] + nums[k] == 0){
//需要保证k在j的右边才可以避免出现重复的三元组
subList = new ArrayList<Integer>();
subList.add(nums[i]);
subList.add(nums[j]);
subList.add(nums[k]);
list.add(subList);
}
}
}
}
}
}
对应的代码:
class Solution {
List<List<Integer>> list = new ArrayList<List<Integer>>();
List<Integer> subList;
public List<List<Integer>> threeSum(int[] nums) {
if(nums == null || nums.length < 3)
return list;
Arrays.sort(nums);//将对数组进行排序,从而减少重复判断
int i,j,k;
for(i = 0; i < nums.length - 2; i++){
k = nums.length - 1;
if(i == 0 || nums[i] != nums[i - 1]) {
for(j = i + 1; j < nums.length - 1 && k > j; j++){
if(j == i + 1 || nums[j] != nums[j - 1]){
while(k > j && nums[i] + nums[j] + nums[k] > 0){
k--;
}
if(k > j && nums[i] + nums[j] + nums[k] == 0){
subList = new ArrayList<Integer>();
subList.add(nums[i]);
subList.add(nums[j]);
subList.add(nums[k]);
list.add(subList);
}
}
}
}
}
return list;
}
}
运行结果: