leetcode 15
链接:力扣 。
题目:
给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有满足条件且不重复的三元组。
注意: 答案中不可以包含重复的三元组。
示例:
输入:nums = [-1, 0, 1, 2, -1, -4],
输出:[ [-1, 0, 1], [-1, -1, 2] ]
思路:
先来看一道经典的双指针问题:在一个有序的序列nums中求两个不同的数使得nums[i] + nums[j] == c (c是一个常数)。
这道题目使用two points解决。设置一个left指针在序列最左端(即下标0)。设置一个right指针在序列最右端(即下标nums.size() - 1)。然后计算当前nums[left] + nums[right]的值。如果nums[left] + nums[right] < c,则把left右移一位;如果nums[left] + nums[right] > c,则把right左移一位;如果nums[left] + nums[right] == c,说明找到了我们需要的二元组,记录下这个二元组后把left右移一位并且把right左移一位。一直重复这个过程,直到left = right结束。
while (left < right) {
if (nums[left] + nums[right] < c) {
left++;
}
else if (nums[left] + nums[right] > c) {
right--;
}
else {
//找到这个二元组,输出
left++;
right--;
}
}
再来看这道三指针的问题。我们先对序列非递减排序,然后用for循环遍历,对于当前元素nums[i],在后面的序列 i + 1 ~ len - 1 中再使用双指针的方法求解使 nums[left] + nums[right] == 0 - nums[i]成立的元组即可。
但是在本题中,我们有一些细节需要处理:
(1)对于非递减的序列,如果当前元素nums[i] > 0,说明之后的元素都比0大了,无论再怎么组合不可能等于0。此时直接退出循环。
(2)本题所给序列中有重复元素,但是要求结果中不需要重复元素。本题得一大难点是怎么去重?最优的方法是在遍历的时候进行去重。这里我们利用到一点:对于有序序列,相等元素一定是连续出现的。对于当前元素nums[i],如果在它的前面的一位已经出现过相等元素,我们直接跳过这次循环进入下一次,这样我们得以消除nums[i]的计算重复。消除nums[left]和nums[right]的计算重复也是用的相同思路。在找到nums[left] + nums[right] == 0 - nums[i]时,我们记录下这次的三元组,然后循环判断nums[left]是否等于它后面的一个元素,如果相等一直往后移动;循环判断nums[right]是否等于它前面的一个元素,如果相等一直往前移动。如此得以消除所有的重复。
参考代码:
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
sort(nums.begin(), nums.end());
vector<vector<int> > result;
int len = nums.size();
for (int i = 0; i < len; i++) {
if (nums[i] > 0) {
break;
}
if (i > 0 && nums[i] == nums[i - 1]) {
continue;
}
int left = i + 1, right = len - 1;
while (left < right) {
if (nums[left] + nums[right] == 0 - nums[i]) {
result.push_back({nums[i], nums[left], nums[right]});
while(left < right && nums[right] == nums[right - 1]) {
right--;
}
while (left < right && nums[left] == nums[left + 1]) {
left++;
}
right--;
left++;
}
else if (nums[left] + nums[right] > 0 - nums[i]) {
right--;
}
else {
left++;
}
}
}
return result;
}
};