LeetCode 15 三数之和

LeetCode 15 三数之和


题目:
给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != j、i != k 且 j != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请
你返回所有和为 0 且不重复的三元组。
注意:答案中不可以包含重复的三元组。


示例 1:

输入: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:

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

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

提示:

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

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/3sum
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

题解:
此题可使用暴力递归和回溯算法都可以解出此题,但是时间复杂度高,此处不给出具体解法
我们使用双指针法巧妙解决此问题,具体流程如下:

例:nums = [-2,-1,0,1,2,2,-1,3]

1.对数组排序
2.出现以下情况直接返回
    a.数组为空
    b.数组最小值大于0
    c.数组最大值小于0
3.遍历数组,i为当前遍历的位置,定义L=i+1 R=数组长度-1,即L为i的下一位,R为数组末尾,然后我们按照以下步骤寻找:
    a.如果nums[i]+nums[L]+nums[R]=0 记录结果且R左移L右移直至与当前L、R所在位置值不同的位置(此步骤就是去重操作)
    b.如果nums[i]+nums[L]+nums[R]>0 说明和太大,R左移直至与当前R所在位置值不同的位置(此步骤就是去重操作)
    c.如果nums[i]+nums[L]+nums[R]<0 说明和太小,L右移直至与当前L所在位置值不同的位置(此步骤就是去重操作)
    d.一旦L,R相遇则此次循环结束
4.结果存储在数组list中

图解:
第一轮遍历
-2-1-101223
iLR
nums[i](-2)+nums[L](-1)+nums[R](3) = 0
记录下结果 [-2,-1,3]
此时list=[[-2,-1,3]]
L右移至与当前值不同的位置
R左移至与当前值不同的位置

-2-1-101223
iLR
nums[i](-2)+nums[L](0)+nums[R](2) = 0
记录下结果 [-2,0,2]
此时list=[[-2,-1,3],[-2,0,2]]
L右移至与当前值不同的位置
R左移至与当前值不同的位置

-2-1-101223
iL,R
L,R相遇,循环结束,i右移至与当前位置值不同的位置开始下一轮循环

第二轮遍历
-2-1-101223
iLR
nums[i](-1)+nums[L](-1)+nums[R](3) = 1
和大于0,R左移至与当前值不同的位置

-2-1-101223
iLR
nums[i](-1)+nums[L](-1)+nums[R](2) = 0
记录下结果 [-1,-1,2]
此时list=[[-2,-1,3],[-2,0,2],[-1,-1,2]]
L右移至与当前值不同的位置
R左移至与当前值不同的位置

-2-1-101223
iLR
nums[i](-1)+nums[L](0)+nums[R](1) = 0
记录下结果 [-1,0,1]
此时list=[[-2,-1,3],[-2,0,2],[-1,-1,2],[-1,0,1]]
L右移至与当前值不同的位置
R左移至与当前值不同的位置
L,R相遇,循环结束,i右移至与当前位置值不同的位置开始下一轮循环

第三轮遍历
-2-1-101223
iLR
注意此时 nums[i](0)+nums[L](1) = 1
后续无论如何移动R将值减少也无法使和等于0,因此结束流程
返回结果 
list=[[-2,-1,3],[-2,0,2],[-1,-1,2],[-1,0,1]]

代码:
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
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> listResult = new ArrayList<>();
Arrays.sort(nums);
if (nums.length <= 2 || nums[0] > 0 || nums[nums.length - 1] < 0) return listResult;
int preI = Integer.MIN_VALUE;
for (int i = 0; i < nums.length; i++) {
if (nums[i] > 0) return listResult;
if (nums[i] == preI) continue;
preI = nums[i];
int L = i + 1;
int R = nums.length - 1;
while (L < R) {
if (nums[i] + nums[L] > 0) break;
int sum = nums[i] + nums[L] + nums[R];
if (sum == 0) {
listResult.add(Arrays.asList(nums[i], nums[L], nums[R]));
do {
L++;
} while (L < R && nums[L] == nums[L - 1]);
do {
R--;
} while (L < R && nums[R] == nums[R + 1]);
} else if (sum < 0) {
while (L < R && nums[i] + nums[L] + nums[R] < 0) {
L++;
}
} else {
while (L < R && nums[i] + nums[L] + nums[R] > 0) {
R--;
}
}
}
}
return listResult;
}