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
思考
三数之和,先借用两数之和的思路,看看如下代码:
public List<List<Integer>> threeSum(int[] nums) {
Set<List<Integer>> result = new HashSet<>();
for (int i = 0; i < nums.length; i++) {
// 重复判断,减少了 while 循环的次数
if (i > 0 && nums[i] == nums[i - 1]) {
continue;
}
List<List<Integer>> list = myTwoSum(nums, -nums[i], i);
for (int j = 0; j < list.size(); j++) {
list.get(j).add(nums[i]);
// 排序,外部 set 去重
Collections.sort(list.get(j));
}
if (!list.isEmpty()) {
result.addAll(list);
}
}
return new ArrayList<>(result);
}
private List<List<Integer>> myTwoSum(int[] nums, int target, int index) {
List<List<Integer>> result = new ArrayList<>();
HashSet<Integer> set = new HashSet<>();
for (int i = index + 1; i < nums.length; i++) {
if (set.contains(target - nums[i])) {
List<Integer> temp = new ArrayList<>();
temp.add(nums[i]);
temp.add(target - nums[i]);
result.add(temp);
}
set.add(nums[i]);
}
return result;
}
这个写法只能说正确,但是效率就不多说呢,能通过。
继续思考,数组的题目,可以想想排序,排序 + 双指针呢?双指针的的思路是在排序的基础上才可以实现的,排序之后遍历每一位,双指针分别是当前遍历的下一位和最后一位,和大于 0 移动 right,和小于 0 移动 left,等于 0 就记录,代码如下:
public List<List<Integer>> threeSum(int[] nums) {
Set<List<Integer>> result = new HashSet<>();
Arrays.sort(nums);
for (int i = 0; i < nums.length; i++) {
// 重复判断,减少了 while 循环的次数
if (i > 0 && nums[i] == nums[i - 1]) {
continue;
}
int left = i + 1;
int right = nums.length - 1;
while (left < right) {
if (nums[i] + nums[left] + nums[right] == 0) {
result.add(Arrays.asList(nums[i], nums[left], nums[right]));
left ++;
right --;
} else if (nums[i] + nums[left] + nums[right] > 0) {
right --;
} else {
left ++;
}
}
}
return new ArrayList<>(result);
}
三数之和可以这么做,那四数之和呢?