15. 三数之和(中等)

1,问题描述

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 。

提示:

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

2,思考过程

​ 首先想到的是之前做过的一道题,两数之和

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
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
import java.util.*;
import java.util.stream.IntStream;

public class _15三数之和 {

// 暴力法
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;
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;
}
// 如果指针重合,随着 b 后续的增加
// 就不会有满足 a+b+c=0 并且 b<c 的 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;
}

// 空间换时间
public List<List<Integer>> threeSum1(int[] nums) {
HashMap<Integer, Integer> map = new HashMap<>();
for (int num : nums) {
if (map.containsKey(num)) {
map.put(num, map.get(num) + 1);
} else {
map.put(num, 1);
}
}

List<List<Integer>> res = new ArrayList<>();
if(map.containsKey(0) && map.get(0) >= 3){
List<Integer> list = new ArrayList<>();
list.add(0);
list.add(0);
list.add(0);
res.add(list);
}

Integer[] array = map.keySet().toArray(new Integer[0]);
for (int i = 0; i < array.length; i++) {
map.put(array[i], map.get(array[i]) - 1);
HashMap<Integer, Integer> mapPre = (HashMap<Integer, Integer>) map.clone();
// 这里也可以修改为双指针
for (int j = i+1; j < array.length; j++) {
mapPre.put(array[j], map.get(array[j]) - 1);
int sub = -array[i] - array[j];
if(mapPre.containsKey(sub) && mapPre.get(sub) > 0){
List<Integer> list = new ArrayList<>();
list.add(array[i]);
list.add(array[j]);
list.add(sub);
res.add(list);
}
mapPre.remove(array[j]);
}
map.remove(array[i]);
}
return res;
}

public static void main(String[] args) {
_15三数之和 threeSum = new _15三数之和();
System.out.println(threeSum.threeSum(new int[]{-4,-2,1,-5,-4,-4,4,-2,0,4,0,-2,3,1,-5,0}));
}

// [[-5,1,4],,[-4,1,3],[-2,-2,4],[-2,1,1],[0,0,0]]
// [-4,0,4]
}

4,思考过程

​ 双指针的算法是当前我知道的最优的