题目描述:给你一个整数数组
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
-10^5 <= nums[i] <= 10^5
1.深搜(暴力
- 排序和Set是为了去重,避免最后结果出现重复的三元组
- 终止条件:如果
cur
列表的大小达到3,检查sum
是否为0。如果是,将当前组合添加到答案ans
中,然后返回 - 递归搜索:在每一层递归中,我们遍历从
index
开始的nums
数组,对于每个数字有两种选择:选择当前数字或不选择当前数字- 如果选择当前数字,则将其添加到
cur
列表中,并更新sum
- 在
index
的基础上递归调用dfs
方法 - 回溯,移除
cur
列表的最后一个元素,以便在下一次递归中使用
- 如果选择当前数字,则将其添加到
class Solution {
static Set<List<Integer>> ans;
public static List<List<Integer>> threeSum(int[] nums) {
if (nums.length == 0) return null;
Arrays.sort(nums);
ans = new HashSet<>();
List<Integer> temp = new LinkedList<>();
dfs(nums, 0, temp, 0);
List<List<Integer>> res = ans.stream().toList();
return res;
}
public static void dfs(int[] nums, int index, List<Integer> cur, int sum) {
if (cur.size() == 3) {
if (sum == 0) ans.add(new LinkedList<>(cur));
return;
}
for (int i = index; i < nums.length; i++) {
cur.add(nums[i]);
dfs(nums, i + 1, cur, sum + nums[i]);
cur.remove(cur.size() - 1);
}
}
}
2.双指针
- k指针从数组的开始向后移动,用于固定三个数中最小的那个数
- i和j指针分别从k的后一个位置和数组的末尾向中间移动,用于找到另外两个数
- 在移动i和j指针时,要注意跳过重复的数字,以避免得到重复的三数组合
- 当找到一个满足条件的三数组合时,将其加入到结果列表中,并继续搜索下一个可能的组合
这种方法的时间复杂度是O(n^2),其中n是数组nums
的长度。这是因为排序的时间复杂度是O(nlogn),然后遍历数组的每个元素,并在内部使用双指针来搜索满足条件的三数组合,这部分的时间复杂度是O(n^2)。
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> res = new ArrayList<>();
if (nums == null || nums.length < 3) return res;
Arrays.sort(nums);
for (int k = 0; k < nums.length - 2; k++) {
if (nums[k] > 0) break;
if (k > 0 && nums[k] == nums[k - 1]) continue;
int i = k + 1, j = nums.length - 1;
while (i < j) {
int sum = nums[k] + nums[i] + nums[j];
if (sum < 0) {
// 跳过所有重复的nums[i]
while (i < j && nums[i] == nums[++i]) ;
} else if (sum > 0) {
// 跳过所有重复的nums[j]
while (i < j && nums[j] == nums[--j]) ;
} else {
res.add(Arrays.asList(nums[k], nums[i], nums[j]));
// 跳过所有重复的nums[i]和nums[j]
while (i < j && nums[i] == nums[++i]) ;
while (i < j && nums[j] == nums[--j]) ;
}
}
}
return res;
}
}