题解 | #三数之和#
三数之和
https://www.nowcoder.com/practice/345e2ed5f81d4017bbb8cc6055b0b711
#include <vector>
class Solution {
public:
vector<vector<int> > threeSum(vector<int> &num) {
//排序
sort(num.begin(),num.end());
int n = num.size();
vector<vector<int>> res;
for(int i = 0;i<n-2;++i)
{
if(num[i] > 0)
break;
if(i!= 0 && num[i] == num[i-1])
continue;
//后续的收尾双指针
int left = i+1;
int right = n-1;
while(left < right)
{
//双指针指向的二值相加大于目标,右指针向左
if(num[left] + num[right] > -num[i])
right--;
//双指针指向的二值相加小于目标,左指针向右
else if(num[left] + num[right] < -num[i])
left++;
else//双指针指向的二值相加为目标,则可以与num[i]组成0
{
vector<int> temp(3);
temp[0] = num[i];
temp[1] = num[left];
temp[2] = num[right];
res.push_back(temp);
//去掉重复的数字,因为结果不能有重复的三元组,所以这三个数字对应的三元组只能出现一次,重复的数字会有同样的结果,因此要去重
while(left + 1 < right && num[left+1] == num[left])
left++;
while(right-1>left && num[right-1] == num[left])
right--;
//双指针向中间收缩,中间可能还会有符合条件的数字组
left++;
right--;
}
}
}
return res;
}
};
思路:
直接找三个数字之和为某个数,太麻烦了,我们是不是可以拆分一下:如果找到了某个数a,要找到与之对应的另外两个数,三数之和为0,那岂不是只要找到另外两个数之和为−a?这就方便很多了。
因为三元组内部必须是有序的,因此可以优先对原数组排序,这样每次取到一个最小的数为a,只需要在后续数组中找到两个之和为−a就可以了,我们可以用双指针缩小区间,因为太后面的数字太大了,就不可能为−a,可以舍弃。
具体做法:
- step 1:排除边界特殊情况。
- step 2:既然三元组内部要求非降序排列,那我们先得把这个无序的数组搞有序了,使用sort函数优先对其排序。
- step 3:得到有序数组后,遍历该数组,对于每个遍历到的元素假设它是三元组中最小的一个,那么另外两个一定在后面。
- step 4:需要三个数相加为0,则另外两个数相加应该为上述第一个数的相反数,我们可以利用双指针在剩余的子数组中找有没有这样的数对。双指针指向剩余子数组的首尾,如果二者相加为目标值,那么可以记录,而且二者中间的数字相加可能还会有。
- step 5:如果二者相加大于目标值,说明右指针太大了,那就将其左移缩小,相反如果二者相加小于目标值,说明左指针太小了,将其右移扩大,直到两指针相遇,剩余子数组找完了。
注:对于三个数字都要判断是否相邻有重复的情况,要去重。
查看19道真题和解析