目录
一、问题描述
给你一个整数数组 nums
,判断是否存在三元组 [nums[i], nums[j], nums[k]]
满足 i != j
、i != k
且 j != k
,同时还满足 nums[i] + nums[j] + nums[k] == 0
。请你返回所有和为 0
且不重复的三元组。
注意:答案中不可以包含重复的三元组。
二、解题思路
- 数组排序:首先将
nums
数组排序,便于后续去重和双指针法的应用。排序的时间复杂度是 O(n log n)。 - 遍历数组:从头到尾遍历数组,每次固定一个数
nums[i]
,然后使用双指针法去寻找剩下两个数nums[j]
和nums[k]
使得nums[i] + nums[j] + nums[k] == 0
。 - 双指针法:
- 设定两个指针,一个指向
i+1
(即left
),一个指向数组末尾(即right
)。 - 如果
nums[i] + nums[left] + nums[right] == 0
,找到一个三元组,保存结果,然后同时移动left
和right
,跳过重复值。 - 如果
nums[i] + nums[left] + nums[right] > 0
,说明右侧数字太大,移动right
指针左移。 - 如果
nums[i] + nums[left] + nums[right] < 0
,说明左侧数字太小,移动left
指针右移。
- 设定两个指针,一个指向
- 去重处理:
- 对于
nums[i]
,如果与上一次遍历的数字相同,则跳过,避免结果中出现重复三元组。 - 在寻找三元组的过程中,遇到重复的
nums[left]
或nums[right]
也要跳过,防止重复的结果。
- 对于
三、代码
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
// 用于存储三元组结果的列表
List<List<Integer>> result = new ArrayList<>();
// 对数组进行排序
Arrays.sort(nums);
// 遍历数组,从第一个元素到倒数第三个
for (int i = 0; i < nums.length - 2; i++) {
// 跳过重复的数,避免重复三元组
if (i > 0 && nums[i] == nums[i - 1]) {
continue;
}
// 初始化双指针
int left = i + 1;
int right = nums.length - 1;
// 双指针法寻找和为0的三元组
while (left < right) {
int sum = nums[i] + nums[left] + nums[right];
if (sum == 0) {
// 找到一个三元组,加入结果列表
result.add(Arrays.asList(nums[i], nums[left], nums[right]));
// 跳过重复的左指针值
while (left < right && nums[left] == nums[left + 1]) {
left++;
}
// 跳过重复的右指针值
while (left < right && nums[right] == nums[right - 1]) {
right--;
}
// 移动指针,继续寻找
left++;
right--;
} else if (sum < 0) {
// 和小于0,左指针右移
left++;
} else {
// 和大于0,右指针左移
right--;
}
}
}
// 返回结果
return result;
}
}
四、复杂度分析
时间复杂度:O(n^2),其中 n
是数组的长度。数组排序需要 O(n log n),双指针法遍历所有可能的三元组则需要 O(n^2)。
空间复杂度:O(1),我们只使用了常量空间来存储变量,除了返回的结果列表以外,没有使用额外的空间。