双指针-15.三数之和-力扣(LeetCode)
一、题目解析
1.不重复
结合示例1,我们能知道[-1,0,1]和[0,1,-1]是相同的三元组,因为他们包含相同的元素。
2.顺序不重要
同样结合示例1 ,输出顺序不重要是指在输出结果时,不关心三元组的顺序;三元组顺序是指三元组内元素排列顺序不重要,例[-1,0,1],也可以是[0,-1,1]、[0,1,-1]等等
二、算法原理
解法1:排序+暴力枚举+set去重 时间复杂度为0(N^3)
毫无疑问这是暴力解法,但我们需要了解暴力解法,便于我们在暴力解法的基础上优化。
解法2:排序+双指针+set去重 时间复杂度为0(N^2)
1.先对数组进行排序
2.固定nums[i],当nums[i]>0,找不到满足的三元组 (排序过后,nums[i]以后都是正数,无法找到负数,使其和为0)
3.在该数后面的区间内,利用“双指针算法”,快速找到两个和为-nums[i]
解法3:排序+双指针+不用set去重 时间复杂度为O(N^2)
为了避免面试时遇到该问题,面试官不允许使用set去重,故多一种解法
1.在双指针区间内找到一种结果之后,left和right指针越过重复元素
2.当使用完一次双指针算法之后,i也需要越过重复元素
细节问题
1.如何保证不漏
在双指针区间内找到一种结果后,不要停,缩小区间继续寻找
2.避免越界问题
在越过重复元素时,需注意越界问题
可以先尝试解法2,然后再去试试解法3,提升自己的代码能力
三、代码示例
解法2:
vector<vector<int>> threeSum(vector<int>& nums){set<vector<int>> s;//set具有去重的性质vector<vector<int>> vv;sort(nums.begin(),nums.end());//排序if(nums[0] == 0 && nums[nums.size()-1] == 0){vv.push_back({0,0,0});//这里会自己转化为vector<int>return vv;}int i = 0,a = 0;int left = 0,right = 0;for(;i<nums.size();i++){a = nums[i];if(a>0) break;//当a<=0时,才存在三元组left = i+1;right = nums.size()-1;if(left>right) break;while(left < right){if(nums[left]+nums[right] < -a) left++;else if(nums[left]+nums[right] > -a) right--;else{s.insert({nums[i],nums[left],nums[right]});left++;right--;}}}for(auto e : s)//范围for,从s中依次取出元素{vv.push_back(e);}return vv;}
解法3:
vector<vector<int>> threeSum(vector<int>& nums){vector<vector<int>> ret;sort(nums.begin(),nums.end());int n = nums.size();for(int i = 0;i<n;){if(nums[i]>0) break;int left = i+1,right = n-1,target = -nums[i];while(left<right){int sum = nums[left]+nums[right];if(sum>target) right--;else if (sum<target) left++;else{ret.push_back({nums[i],nums[left],nums[right]});left++,right--;//去重操作while(left<right && nums[left] == nums[left-1]) left++;while(left<right && nums[right] == nums[right+1]) right--;}}//去重ii++;//这里代替for循环++功能,所以for循环处为空while(i < n && nums[i] == nums[i-1]) i++;}return ret;}