📝 问题描述
给定一个整数数组 nums,找出所有和为 0 且不重复的三元组 [nums[i], nums[j], nums[k]],其中 i != j != k。
注意:答案中不可包含重复的三元组。
🔍 解题思路
这个问题的关键挑战在于:
-
需要找到所有符合条件的三元组
-
结果不能包含重复三元组
-
数组可能很大(长度达3000)
最直接的方法是使用三重循环,但时间复杂度为 O(n³),效率较低。我们可以通过排序和双指针技术将复杂度降至 O(n²)。
🧮 算法步骤
-
排序:首先对数组进行排序,这样相同的元素会相邻,便于去重
-
固定第一个数:遍历排序后的数组,固定第一个数
nums[i] -
双指针:使用两个指针
j和k,分别从i+1和数组末尾向中间移动 -
计算三数之和:
-
如果和为0,记录结果并移动双指针
-
如果和小于0,左指针右移(增加和)
-
如果和大于0,右指针左移(减小和)
-
-
去重处理:对于所有指针,当遇到相同元素时,跳过以避免重复结果
💻 代码实现
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
int i = 0;
List<List<Integer>> result = new ArrayList<>();
// 先对数组进行排序
Arrays.sort(nums);
while (i < nums.length - 2) {
// 如果第一个数大于0,因为数组已排序,后面不可能有三数之和等于0的情况
if (nums[i] > 0) break;
int j = i + 1; // 左指针
int k = nums.length - 1; // 右指针
while (j < k) {
int sum = nums[i] + nums[j] + nums[k];
if (sum == 0) {
// 找到一组解
List<Integer> triplet = new ArrayList<>();
triplet.add(nums[i]);
triplet.add(nums[j]);
triplet.add(nums[k]);
result.add(triplet);
// 跳过重复元素
while (j < k && nums[j] == nums[j + 1]) j++;
while (j < k && nums[k] == nums[k - 1]) k--;
// 继续查找其他可能的解
j++;
k--;
} else if (sum < 0) {
// 和太小,左指针右移
j++;
} else {
// 和太大,右指针左移
k--;
}
}
// 跳过重复的第一个数
while (i < nums.length - 2 && nums[i] == nums[i + 1]) i++;
i++;
}
return result;
}
}
⚙️ 代码逻辑分解
1. 初始化和排序
int i = 0;
List<List<Integer>> result = new ArrayList<>();
Arrays.sort(nums);
-
创建结果列表和起始索引
-
对数组排序,为后续操作做准备
2. 外层循环:固定第一个数
while (i < nums.length - 2) {
// 循环体
// 跳过重复的第一个数
while (i < nums.length - 2 && nums[i] == nums[i + 1]) i++;
i++;
}
-
遍历数组,固定三元组的第一个元素
-
循环结束后跳过重复元素,避免产生重复结果
3. 内层循环:双指针查找
int j = i + 1;
int k = nums.length - 1;
while (j < k) {
int sum = nums[i] + nums[j] + nums[k];
// 处理各种情况
}
-
使用双指针技术,在区间
[i+1, n-1]内寻找符合条件的两个数
4. 三种和的情况处理
if (sum == 0) {
// 添加结果并跳过重复元素
// ...
j++;
k--;
} else if (sum < 0) {
j++;
} else {
k--;
}
-
根据三数之和与0的比较结果,决定如何移动指针
🔄 去重逻辑
代码中有三处去重处理:
-
找到解后,跳过左指针的重复元素:
while (j < k && nums[j] == nums[j + 1]) j++; -
找到解后,跳过右指针的重复元素:
while (j < k && nums[k] == nums[k - 1]) k--; -
每轮外循环结束后,跳过第一个数的重复元素:
while (i < nums.length - 2 && nums[i] == nums[i + 1]) i++;
⏱️ 复杂度分析
-
时间复杂度:O(n²)
-
排序需要 O(n log n)
-
双层循环需要 O(n²)
-
总体为 O(n²)
-
-
空间复杂度:O(1)(不考虑存储结果所需空间)
-
仅使用常数额外空间
-
🌟 关键技巧
-
排序 + 双指针:这是解决此类问题的常用技巧
-
提前结束条件:当
nums[i] > 0时可提前终止循环 -
去重处理:在各个指针移动时避免处理重复元素
📊 示例演示
以 nums = [-1,0,1,2,-1,-4] 为例:
-
排序后:
[-4,-1,-1,0,1,2] -
固定第一个数
-4:-
双指针搜索
[-1,-1,0,1,2],无解
-
-
固定第一个数
-1:-
找到
[-1,0,1],和为0,记录 -
找到
[-1,-1,2],和为0,记录
-
-
固定第一个数
-1(重复,跳过) -
固定第一个数
0:-
无新解
-
-
返回结果:
[[-1,0,1],[-1,-1,2]]
通过排序和双指针技术,我们成功高效地解决了三数之和问题,找出了所有不重复的三元组。
884

被折叠的 条评论
为什么被折叠?



