题目
给你一个整数数组 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
一、题目分析
这道题是求给定整数数组中所有满足三数之和为 0 的不重复三元组。
二、解题思路
- 首先对输入数组进行排序,排序后的数组可以方便地进行双指针遍历以及避免重复结果。
- 然后使用三层循环暴力求解的方法时间复杂度较高,这里采用双指针法进行优化。
- 最外层循环遍历数组,固定一个数
nums[k]
,将问题转化为在剩余数组中寻找两数之和等于-nums[k]
的问题。 - 内层使用双指针
i
和j
,分别从k + 1
和数组末尾开始向中间移动。 - 根据三数之和与 0 的大小关系来调整指针位置。
- 如果和小于 0,说明需要增大和,所以将左指针
i
向右移动。 - 如果和大于 0,说明需要减小和,所以将右指针
j
向左移动。 - 如果和等于 0,则找到了一组满足条件的三元组,将其加入结果列表中。
- 如果和小于 0,说明需要增大和,所以将左指针
- 为了避免重复结果,在循环过程中,如果当前数与前一个数相同,则跳过。
三、代码解释
Arrays.sort(nums);
:对输入数组进行排序,为后续的双指针法做准备。if(nums[k] > 0) break;
:如果当前固定的数已经大于 0,那么后面的数也都大于 0,不可能再找到三数之和为 0 的情况,所以直接跳出循环。if(k > 0 && nums[k] == nums[k - 1])continue;
:避免重复结果,如果当前数与前一个数相同,则跳过。while(i < j && nums[i] == nums[++i]);
和while(i < j && nums[j] == nums[--j]);
:在移动指针的过程中,如果当前指针指向的数与前一个数相同,继续移动指针,以避免重复结果。ans.add(new ArrayList<Integer>(Arrays.asList(nums[k], nums[i], nums[j])));
:将找到的满足条件的三元组加入结果列表中。
四、时间复杂度和空间复杂度
- 时间复杂度:排序操作需要 的时间,双指针遍历需要 的时间,所以总体时间复杂度为 。
- 空间复杂度:如果不考虑输出结果占用的空间,只考虑在程序执行过程中的辅助空间,由于只使用了有限的几个变量,所以空间复杂度为 。如果考虑输出结果占用的空间,空间复杂度为 ,其中 是满足条件的三元组的数量。
func threeSum(nums []int) [][]int {
var ans = make([][]int, 0)
n := len(nums)
sort.Ints(nums)
for k := 0; k < n-2; k++ {
if nums[k] > 0 {
break
}
if k > 0 && nums[k] == nums[k-1] {
continue
}
i, j := k+1, n-1
for i < j {
sum := nums[k] + nums[i] + nums[j]
if sum > 0 {
for i < j && nums[j-1] == nums[j] {
j--
}
j--
} else if sum < 0 {
for i < j && nums[i+1] == nums[i] {
i++
}
i++
} else {
ans = append(ans, []int{nums[k], nums[i], nums[j]})
for i < j && nums[j-1] == nums[j] {
j--
}
for i < j && nums[i+1] == nums[i] {
i++
}
j--
i++
}
}
}
return ans
}
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> ans = new ArrayList<>();
Arrays.sort(nums);
int n = nums.length;
for(int k = 0; k < n - 2; k++){
if(nums[k] > 0) break;
if(k > 0 && nums[k] == nums[k - 1])continue;
int i = k + 1, j = n - 1;
while(i < j){
if (nums[j] == 1){
System.out.println(nums[i]);
}
int sum = nums[k] + nums[i] + nums[j];
if(sum < 0) {
while(i < j && nums[i] == nums[++i]);
} else if (sum > 0) {
while(i < j && nums[j] == nums[--j]);
} else {
ans.add(new ArrayList<Integer>(Arrays.asList(nums[k], nums[i], nums[j])));
while(i < j && nums[j] == nums[--j]);
while(i < j && nums[i] == nums[++i]);
}
}
}
return ans;
}
}