15. 三数之和

1. 题目

给你一个整数数组 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 。

提示:

  • 3 <= nums.length <= 3000
  • -105 <= nums[i] <= 105

2. 思路

  • 首先对数组进行排序,使得数组整体从左到右递增,初始化三个数组指针,分别位于l = 0, m = 1, r = nums.length - 1

  • 执行循环前,先判断nums[0] + nums[1] + nums[2]的结果大小,如果计算结果大于0,又由于是递增,后续数值不会再比前3个数值小,因此确定该数组无结果集,可直接返回空

  • 同时判断后三位数值nums[length - 1] + nums[length - 2] + nums[length - 3]的结果大小,如果计算结果小于0,由于数组最大值总和都比0小,且整个数组也不会再有比后三位值再大的数了,因此说明该数组无结果集,可直接返回空

  • 整体的解题思路采用固定l指针,逐步移动m 指针和r指针的方式索引出结果集

  • 在二层循环的过程中,需要确保m指针小于r指针

    • nums[l] + nums[m] + nums[r]的结果和为0时,将其数值进行记录
    • nums[l] + nums[m] + nums[r]的结果和大于0时,则将r指针向左移动一位,以试图减小结果和
    • nums[l] + nums[m] + nums[r]的结果和小于0时,则将m指针向右移动一位,以试图加大结果和
  • 由于在过程中需要保证结果集唯一,因此在移动r指针和m指针时,需要进行去重操作

  • 在二层循环执行结束后,l指针进行去重后向右移动1位,同时初始化m指针为l + 1,初始化r指针为nums.length - 1

3. 代码

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
50
51
52
53
54
55
56
57
58
59
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
var result = new ArrayList<List<Integer>>();
var l = 0;
var m = 1;
var r = nums.length - 1;

Arrays.sort(nums);

if (nums[l] + nums[l + 1] + nums[l + 2] > 0) {
return result;
}

if (nums[r] + nums[r - 1] + nums[r - 2] < 0) {
return result;
}

while(l < nums.length) {
while (m < r) {
var sum = nums[l] + nums[m] + nums[r];
if (sum == 0) {
result.add(List.of(nums[l], nums[m], nums[r]));
m++;
while (r > m && nums[m] == nums[m - 1]) {
m++;
}

r--;
while (r > m && nums[r] == nums[r + 1]) {
r--;
}
}

if (sum > 0) {
r--;
while (r > m && nums[r] == nums[r + 1]) {
r--;
}
}

if (sum < 0) {
m++;
while (r > m && nums[m] == nums[m - 1]) {
m++;
}
}
}

while (l + 1 < nums.length && nums[l] == nums[l + 1]) {
l++;
}
l++;
m = l + 1;
r = nums.length - 1;
}

return result;
}
}

4. 复杂度

  • 时间复杂度: O(n²)
  • 空间复杂度: O(1)

image-20230814104743044