15.三数之和

给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != ji != kj != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请

你返回所有和为 0 且不重复的三元组。

注意:答案中不可以包含重复的三元组。

示例 1:

1
2
3
4
5
6
7
8
输入:nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2],[-1,0,1]]
解释:
nums[0] + nums[1] + nums[2] = (-1) + 0 + 1 = 0
nums[1] + nums[2] + nums[4] = 0 + 1 + (-1) = 0
nums[0] + nums[3] + nums[4] = (-1) + 2 + (-1) = 0
不同的三元组是 [-1,0,1] 和 [-1,-1,2] 。
注意,输出的顺序和三元组的顺序并不重要。

示例 2:

1
2
3
输入:nums = [0,1,1]
输出:[]
解释:唯一可能的三元组和不为 0

示例 3:

1
2
3
输入:nums = [0,0,0]
输出:[[0,0,0]]
解释:唯一可能的三元组和为 0

三数之和要求的是返回三元组,而不是下标,也就是说和之前的双数之和截然不同,不好用Hash

但是直接返回值使我们考虑忽略掉数组之前的顺序,先对数组进行sort排序,从而有使用双指针的空间,second不断增大,将thrid不断缩小,时间复杂度从$O(N^3)$降到了$O(N^2)$

使用continue以替代while,可以降低代码的复杂度,规避while条件越界问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
// 获取数组长度
int n = nums.length;
// 首先对数组进行排序,以便后续操作
Arrays.sort(nums);
// 用于存储最终答案的列表
List<List<Integer>> ans = new ArrayList<List<Integer>>();

// 枚举第一个数a
for (int first = 0; first < n; ++first) {
// 跳过重复的数,避免结果中出现重复的三元组
if (first > 0 && nums[first] == nums[first - 1]) {
continue;
}
// 初始化第三个数c的指针为数组的最右端
int third = n - 1;
// 目标数为当前第一个数a的相反数,这样保证了三数之和为0
int target = -nums[first];

// 枚举第二个数b
for (int second = first + 1; second < n; ++second) {
// 同样跳过重复的数
if (second > first + 1 && nums[second] == nums[second - 1]) {
continue;
}
// 确保第二个数b的指针在第三个数c的指针的左侧
while (second < third && nums[second] + nums[third] > target) {
--third; // 移动第三个数c的指针
}
// 如果第二个数b的指针和第三个数c的指针重合,说明没有满足条件的三元组,可以退出内层循环
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;
}
}