【Leetcode刷题记录】15.三数之和
15. 三数之和
给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]]
满足 i != j、i != k 且 j != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。
请你返回所有和为 0 且不重复的三元组。
注意:答案中不可以包含重复的三元组。
📑排序+双指针
对于这道题,最先想到的可能就是三重循环暴力枚举所有可能的三元组,但是这样做肯定是超时且不优雅的。因此要寻求一个更优化的解法,就是排序加双指针。
先对数组进行排序,这样做不仅有助于避免结果中的重复三元组,还为后续应用双指针奠定了基础。一旦数组被排完序,我们可以遍历数组中的每一个元素nums[i]
将其作为潜在的三元组的第一个元素,对于每一个nums[i]
,现在的目标是找到另外两个元素nums[j]
和nums[k]
(k>j>i
),使得nums[j]+nums[k]=-nums[i]
接下来就可以使用双指针来实现这一目标。在每次迭代中,我们设置两个指针:left
指针初始化为i+1
,指向当前元素之后的第一个位置;righ
t指针则指向数组的最后一个元素。通过移动这两个指针,可以尝试找到满足条件的其他两个数。如果left
指针和right
指针所指向的数之和小于-nums[i]
,我们就增加left指针以尝试获得更大的值;反之,如果这个和大于-nums[i]
,我们就减少right
指针以减小总和。当找到一组满足条件的三元组时,我们将它添加到结果集中。为了避免输出重复的答案,在发现一个有效的三元组之后,我们需要跳过所有与当前left或right指针对应的相同元素。
代码实现
vector<vector<int>> threeSum(vector<int>& nums) {
sort(nums.begin(),nums.end());
int n = nums.size();
vector<vector<int>> res;
for (int i = 0; i < n - 2; i++) {
if (i > 0 && nums[i] == nums[i - 1])
continue;
int l = i + 1, r = n - 1;
while (l < r) {
if (nums[i] + nums[l] + nums[r] < 0) {
l += 1;
} else if (nums[i] + nums[l] + nums[r] > 0) {
r -= 1;
} else {
res.push_back({nums[i], nums[l], nums[r]});
// 去重
while (l < r && nums[l] == nums[l + 1]) {
l += 1;
}
while (l < r && nums[r] == nums[r - 1]) {
r -= 1;
}
l += 1;
r -= 1;
}
}
}
return res;
}
这里还有可以优化的点
- 如果当前元素与接下来两个最小的元素之和大于0,则直接终止循环
- 如果当前元素与最大的两个元素之和小于0,则跳过此次循环
下面是稍微优化后的代码
vector<vector<int>> threeSum(vector<int>& nums) {
sort(nums.begin(), nums.end());
int n = nums.size();
vector<vector<int>> res;
for (int i = 0; i < n - 2; i++) {
// 提前终止条件1: 如果当前元素与接下来两个最小的元素之和大于0,则直接终止循环
if (nums[i] + nums[i + 1] + nums[i + 2] > 0) break;
// 提前终止条件2: 如果当前元素加上最大的两个元素之和仍小于0,则跳过此次迭代
if (nums[i] + nums[n - 1] + nums[n - 2] < 0) continue;
// 跳过重复项
if (i > 0 && nums[i] == nums[i - 1]) continue;
int l = i + 1, r = n - 1;
while (l < r) {
if (nums[i] + nums[l] + nums[r] < 0) {
l += 1;
} else if (nums[i] + nums[l] + nums[r] > 0) {
r -= 1;
} else {
res.push_back({nums[i], nums[l], nums[r]});
// 去重
while (l < r && nums[l] == nums[l + 1]) l += 1;
while (l < r && nums[r] == nums[r - 1]) r -= 1;
l += 1;
r -= 1;
}
}
}
return res;
}