LeetCode_51_3sum


1. question: 三数之和(中等)

给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有和为 0 且不重复的三元组。

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

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/3sum

示例 1:

1
2
输入:nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2],[-1,0,1]]

示例 2:

1
2
输入:nums = []
输出:[]

示例 3:

1
2
输入:nums = [0]
输出:[]

提示:

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

2. answers

这道题肯定暴力破解肯定会超时的。那么能不能用两数之和的思路来做呢?比如遍历数组,逐个以当前元素作为基础元素,求解两数之和为该基础元素的反数组合。这样做的话,得需要去重,所以这样做仍然是超时了。

其实仔细想想,超时的原因就是存在许多无用的判断。如果可以根据某些现象来去除掉一些无用判断就好了。可以将数组进行排序(以升序为例),逐个以当前元素作为基础元素,以首尾双指针法对当前元素的后续元素进行遍历求解组合。如果当前组合大于0,那么肯定是尾指针向前移动,因为已经排序了,此时首指针向后移动就是无用的组合。小于0同理。之后再去重。

去重的话,每生成一个结果,就对当前已有所有结果进行比较,因为排序了,所以产生的结果也是有序的,只需要逐个比较元素即可。

代码如下所示:

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
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
public class Solution_0049_03 {

public static List<List<Integer>> threeSum(int[] nums) {

// 保存结果
List<List<Integer>> result = new ArrayList<>();

if(nums.length < 3) return result;

// 对数组排序(升序)
Arrays.sort(nums);
for (int i : nums) {
System.out.println(i);
}

int base = 0;
int head;
int tail;

int tempResult;

while(base <= nums.length - 2 - 1) {

head = base + 1;
tail = nums.length - 1;

// 中指针遍历
while(head < tail) {

tempResult = nums[base] + nums[head] + nums[tail];

if(tempResult == 0) {
List<Integer> temp = new ArrayList<>();

// 注意,此时加入的是有序的,所以后续比较是否重复的时候,可以根据元素来判断
temp.add(nums[base]);
temp.add(nums[head]);
temp.add(nums[tail]);

// 判断是否已经有该结果了
if(!hasList(result, temp)) {
result.add(temp);
}

// 注意,满足条件后向后指针即可。
head += 1;
}

if(tempResult < 0) {
head += 1;
}

if(tempResult > 0) {
tail -= 1;
}

}

// 一轮循环完之后,基础指针向后移动一位
base += 1;
}

return result;
}

// 判断是否已经包含结果了。
public static boolean hasList(List<List<Integer>> result, List<Integer> temp) {

int sum;

for(List<Integer> item : result) {

sum = 0;

// 可逐个比较元素即可
for (int i = 0; i < temp.size(); i++) {

if (item.get(i).equals(temp.get(i))) {
sum += 1;
} else {
break;
}
}

if(sum == 3) {
return true;
}
}

return false;
}

public static void main(String[] args) {
System.out.println();

int[] nums = {-1, 0, 1, 2, -1, -4};

List<List<Integer>> result = threeSum(nums);

System.out.println("结果如下所示:");

for (List<Integer> temp : result ) {

for (Integer integer : temp) {
System.out.print(integer + "\t");
}

System.out.println();
}
}
}

怎么说呢,上述方法虽然在LeetCode上通过了,但是耗时非常大。其实排序很大程度上减少了无用比较,但是在去重方面仍然存在较大开销。那么什么时候会出现重复结果呢?因为已经排序了,所以相同元素肯定出现在相邻位置上,那么我们在移动指针的时候,只需要判断移动前后的两个元素是否相同,如果相同,则继续移动,这样就可以避免出现重复结果了。

注意,因为排序了,所以重复元素只有可能出现在相邻位置。可以看到,对数组排序的作用还是挺大的。代码如下所示:

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
public class Solution_0049_04 {

public static List<List<Integer>> threeSum(int[] nums) {

List<List<Integer>> result = new ArrayList<>();

if(nums.length < 3) return result;

// 对数组进行排序
Arrays.sort(nums);

// 设置三指针
int base = 0;
int head;
int tail;

int tempResult;

while(base <= nums.length - 1 - 2) {

System.out.println(base);

// 如果当前基础指针元素已经大于0了,说明后面的元素一定也是大于0的,肯定没有结果了,直接返回即可。
if(nums[base] > 0) {
return result;
}


// 如果和上一个基础指针一样,那么就无需再判断,防止避免出现重复结果。
// 注意,因为已经排序了,所以相同元素只可能出现在相邻位置上,所以这样判断是合理的,肯定能避免出现重复元素
if(base != 0) {
while(nums[base] == nums[base-1] && base <= nums.length - 1 - 2)
base ++;
}

head = base + 1;
tail = nums.length - 1;

while(head < tail) {

tempResult = nums[base] + nums[head] + nums[tail];

if(tempResult == 0) {
List<Integer> temp = new ArrayList<>();

temp.add(nums[base]);
temp.add(nums[head]);
temp.add(nums[tail]);

result.add(temp);

head++;
tail--;

// 注意,要判断移动的元素是否和上一个元素一样,如果一样,则继续移动,防止出现重复结果。
while(nums[head] == nums[head-1] && head < tail)
head ++;

while(nums[tail] == nums[tail+1] && head < tail)
tail --;
}

if(tempResult < 0) {
head ++;
}

if(tempResult > 0) {
tail --;
}
}

// 注意,如果当前base指针和下一个base指针一样,那么就直接略过,因为后续每次head和tail都会重新取值
base += 1;
}

return result;
}

public static void main(String[] args) {

int[] nums = {-1, 0, 1, 2, -1, -4};

List<List<Integer>> result = threeSum(nums);

System.out.println("结果如下所示:");

for (List<Integer> temp : result ) {

for (Integer integer : temp) {
System.out.print(integer + "\t");
}

System.out.println();
}
}
}

3. 备注

参考力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台 (leetcode-cn.com)代码随想录 (programmercarl.com)


文章作者: 浮云
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 浮云 !
  目录