15. 三数之和
难度 中等
给你一个包含 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
题解
这道题看着提容易理解,但是实际上不是很好解,三次方不满足时间复杂度,所以必须想办法优化。像官方题解,先选定一个数,然后还需要选定两个数,这样就像167. 两数之和 II - 输入有序数组了,但是还有一个问题,这里的数字可以重复,出现同样的答案,需要去重。
-
第一种做法,找出所有结果之后,然后去重。这种做法解题过程比较简单,但是去重效率低。
-
第二种做法,在解题的时候去重。反过来,解题过程比较复杂,不需要去重。
那在解题的过程怎么去重,如果选的第一个数是1,第二个数也是1,第三个数是-2,假设数组是[1,1,1,1,1,1,1,1,1,-2],那么会有很多重复的结果。如果我们限制选了第一个数,之后,我们第二个数的选取要加以限制,必须与前一个数不相等。比如,第一个数选取了下标为0的数1,第二个数选取了下标为1的数1,第三个数选取了最后一个数-2,有了第一个结果[1,1,-2],那么第二个数选取范围从1到8之间的所有1构成的结果都是一样的,我们只要加以限制不可以选取与前一个数相同的数就行(nums[second] != nums[second - 1])。
类似的,第一个数也是这样,不能选取与前一个数相同的数,因为前一个数已经枚举完所有这个数可能的结果。
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
int n = nums.length;//数组长度
Arrays.sort(nums);//排序,变为升序数组
List<List<Integer>> ans = new ArrayList<List<Integer>>();//存储答案
for(int first = 0; first < n; first++){//枚举第一个数
if(first > 0 && nums[first] == nums[first - 1]){//如果枚举的数和前一个数相同,跳过循环
continue;
}
int third = n - 1;//从后面开始枚举
int target = -nums[first];//a+b+c=0,移项,b+c=-a
for(int second = first + 1; second < n; second++){//枚举第二个数
if(second > first + 1 && nums[second] == nums[second - 1]){//如果枚举的数和前一个数相同,跳过循环
continue;
}
while(second < third && nums[second] + nums[third] > target){//如果两个数之后大于target,第三个数后迁移
--third;
}
if(second == third){//下标相同,结束循环
break;
}
if(nums[second] + nums[third] == target){//找到结果
List<Integer> list = new ArrayList<Integer>();
list.add(nums[first]);
list.add(nums[second]);
list.add(nums[third]);
ans.add(list);
}
}
}
return ans;
}
}