15. 三数之和
给你一个包含 n
个整数的数组 nums
,判断 nums
中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有和为 0
且不重复的三元组。
注意:答案中不可以包含重复的三元组。
示例 1:
输入:nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2],[-1,0,1]]
示例 2:
输入:nums = []
输出:[]
示例 3:
输入:nums = [0]
输出:[]
提示:
0 <= nums.length <= 3000
-105 <= nums[i] <= 105
方法一:
本题和 Easy | LeetCode 1. 两数之和 | 排序+双指针 | HaspMap 类似, 也可使用排序加双指针的方式。先排序, 然后从到右扫描数组, 找三数之和实际上就是找当前位置后面有没有两数之和为 target - num 的数组。
由于题目不允许重复值。所以有两处需要跳过重复值。
- 在计算三数之和时, 对于当前值, 要跳过后面的重复值
- 在计算两数之和时, 由于采用的是双指针法, 既要跳过左指针的重复值, 也要跳过右指针的重复值。
详细代码如下:
public List<List<Integer>> threeSum(int[] nums) {
// 先排序
Arrays.sort(nums);
List<List<Integer>> res = new ArrayList<>();
int target = 0;
int i = 0;
while (i < nums.length-1) {
int firstValue = nums[i];
// 将三数之和问题 转化为 两数之和 问题
List<List<Integer>> twoSum = twoSum(nums, i + 1, target - firstValue);
if (twoSum.size() > 0) {
twoSum.forEach(twoSumList -> {
List<Integer> threeSum = new ArrayList<>();
threeSum.add(firstValue);
threeSum.addAll(twoSumList);
res.add(threeSum);
});
}
// 同样扫描数组时, 要跳过重复值
while (i < nums.length - 1 && nums[i] == firstValue) {
i++;
}
}
return res;
}
public List<List<Integer>> twoSum(int[] nums, int start, int target) {
int left = start, right = nums.length -1;
List<List<Integer>> res = new ArrayList<>();
// 两数之和问题, 双指针法
while (left < right) {
int sum = nums[left] + nums[right];
int leftValue = nums[left], rightValue = nums[right];
if (sum < target) {
// 当双指针之和小于目标值时, 左指针向前走
left++;
} else if (sum > target) {
// 当双指针之和大于目标值时, 右指针向左走
right--;
} else {
// 恰好等于目标值时, 先把当前答案添加进去
List<Integer> twoSum = new ArrayList<>();
twoSum.add(leftValue);
twoSum.add(rightValue);
res.add(twoSum);
// 然后跳过左边的重复值, 因为题目不允许重复
while (left < right && nums[left] == leftValue) {
left++;
}
// 跳过右边的重复值
while (left < right && nums[right] == rightValue) {
right--;
}
}
}
return res;
}