开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第11天,点击查看活动详情
题目描述
给你一个整数数组 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] <= 10^5
解题思路
题目长度为3000,表示我们可以用双重循环。用暴力的三重循环判断三者相加是否等于target直接会超时。 做两数之和的时候我们是先用set来保存数字,再遍历一遍看是否有与之对应的值;
这题我们也可以是这个思路,只不过稍有不同,先将数组排序,在一重循环里先确定一个值num,接着任务就是去找两数之后为-num的值,因为已经排序了,我们就可以用双指针left,right,如果nums[left]+nums[right]大于-num,就把right下标减一,小于就把left+1,等于-num表示找到一组值。
代码
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
int n = nums.length;
Arrays.sort(nums);
List<List<Integer>> ans = new ArrayList<>();
for (int i = 0; i < n; i++) {
if (i > 0 && nums[i] == nums[i - 1]) continue; // 当前数和上一个数一样,直接跳过
int target = -nums[i]; // 现在的目的就是要找两个数的和为target的了
int left = i + 1,right = n - 1; // 双指针的下标
while (left < right) {
// 也是当前数等于上一个数
if (left > i + 1 && nums[left] == nums[left - 1]) {
left ++;
continue;
}
if (right < n - 1 && nums[right] == nums[right + 1]) {
right --;
continue;
}
// 当前的数大于目标值,减小当前数,right右移
if (nums[left] + nums[right] > target) {
right--;
} else if(nums[left] + nums[right] < target) {
left ++;
} else { // 找到三数了
List<Integer> list = new ArrayList<>();
list.add(nums[i]);
list.add(nums[left]);
list.add(nums[right]);
ans.add(list);
left++;
right--;
}
}
}
return ans;
}
}