#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:如果二者相加大于目标值,说明右指针太大了,那就将其左移缩小,相反如果二者相加小于目标值,说明左指针太小了,将其右移扩大,直到两指针相遇,剩余子数组找完了。
注:对于三个数字都要判断是否相邻有重复的情况,要去重。