15.三数之和
一、第一轮思路
三层for循环遍历(真有你的)
二、第二轮思路
以前隐约看到过一个哈希表方法,弄一个map<vector,int>,先储存存两数的数组和两数之和,通过哈希表找到0-x的值,复杂度O(n^2)。
三、看看题解
我又生产了啥垃圾这是?
主流的做法似乎是排序后使用双指针,不过笔者的两个思路也不算完全八杆子打不着。本题的重点与两数之和不同,注意两数之和题干“你可以假设每种输入只会对应一个答案”,但三数之和题干“返回所有和为 0
且不重复的三元组”,所以笔者两个思路的问题核心都在“去重”这件事上。
对于三层for循环遍历,无法去重,所以该思路不再考虑。
哈希表的方法属于有些可行但没必要,把符合条件的三元组放进vector中,然后再去重,这样是非常费时的,很容易超时,也是这道题目通过率如此之低的根源所在。去重的过程不好处理,有很多小细节,如果在面试中很难想到位。
可以发现,如果固定了前两重循环枚举到的元素 a 和 b,那么只有唯一的 c 满足 a+b+c=0。当第二重循环往后枚举一个元素 b′时,由于 b′>b,那么满足 a+b′+c′=0的 c ′一定有 c′<c,即 c在数组中一定出现在 c 的左侧。也就是说,可以从小到大枚举 b,同时从大到小枚举 c,即第二重循环和第三重循环实际上是并列的关系。有了这样的发现,就可以保持第二重循环不变,而将第三重循环变成一个从数组最右端开始向左移动的指针,即为双指针解法。
四、解题思路
1.双指针部分
对数组先进行排序
定义i j k ,两层循环,外层遍历i,内层移动 j k ,找nums[i]=nums[j]+nums[k],
若nums[j]+nums[k]小了就右移 j ,nums[j]+nums[k]大了就左移 k
2.去重想法
出现符合条件的 j k 后,j++,k–,判断j+1和k-1,与现在相等直接跳过(略有问题,请看4.4.2)
3.代码实战
ac代码如下:
vector<vector<int>> threeSum(vector<int>& nums) {
sort(nums.begin(), nums.end());
vector<vector<int>>result;
int size=nums.size();
for(int i=0;i<size&&nums[i]<=0;i++){
if(i!=0)
if(nums[i]==nums[i-1])
continue;
int j=i+1;
int k=size-1;
while(j<k){
long long number1=nums[j]+nums[k];
if(-nums[i]>number1){
j++;
}
else if(-nums[i]<number1){
k--;
}
else{
result.push_back({nums[i],nums[j],nums[k]});
while(j<k&&nums[j]==nums[j+1]) //if改成while
j++;
while(j<k&&nums[k]==nums[k-1])
k--;
j++;
k--;
}
}
}
return result;
}
4.遇到的问题
(1)编译相关
在写for循环条件时,一开始将 i < size && nums[i] <= 0写为了nums[i] <= 0 && < size,在力扣执行会数组越界
(2)去重相关
在对jk去重时,一开始的代码为
if(j<k&&nums[j]==nums[j+1]) //if改成while
j++;
if(j<k&&nums[k]==nums[k-1])
k--;
执行后有一种情况,如果数组两侧都是三个及以上的连续相同数子,如-5,1,1,1,3,4,4,4
j k经历一轮if的去重不够完全,需要修改成while
5.做题感受
复健运动第三题,还遇到了很多零碎细节的小问题,不赘述了,痛苦。
五、对双指针解法的思考
一般是用于一个有序数组,如果两个指针不会相交即为滑动窗口