leetcode 18 四数之和
leetcode 259 较小的三数之和
这三道题目都是一个类型,主要就是利用有序数组与双指针的一些技巧;
首先给出一个基本模板,如何在一个有序数组中找到两数之和为一目标值target;
int l=0,r=nums.size()-1;
while(l<r)
{
if(nums[l]+nums[r]==target)
{
//根据不同的要求,进行一些必要的操作
//考虑是否去重
}
else if(nums[l]+nums[r]<target)
++l;
else
--r;
}
1.首先看leetcode 15 三数之和
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
vector<vector<int>>res;
if(nums.size()<3)return{};
sort(nums.begin(),nums.end());
for(int i=0;i<nums.size()-2;++i)
{
int l=i+1,r=nums.size()-1;
if(i>0&&nums[i]==nums[i-1])continue;//去重复
while(l<r)
{
auto sum=nums[l]+nums[r]+nums[i];
if(0==sum)
{
res.push_back({nums[i],nums[l],nums[r]});
while(l+1<r&&nums[l]==nums[l+1])//去重复
++l;
while(r-1>l&&nums[r]==nums[r-1])//去重复
--r;
++l;
--r;
}
else if(sum<0)
++l;
else
--r;
}
}
return res;
}
};
核心部分就是while(l<r)
那个循环,也就是上述的模板,主要有两个关键点;
1.当我们找到和一个目标值的子数组时,需要去重复,也就是将l
不断的右移动,r
不断向左移动;
2.外层循环对nums[i]
,也有同样的去重复操作;
2.leetcode16 最接近的三数之和
class Solution {
public:
int threeSumClosest(vector<int>& nums, int target) {
sort(nums.begin(),nums.end());
int mind=nums[0]+nums[1]+nums[2];
for(int i=0;i<=nums.size()-3;++i)
{
int l=i+1,r=nums.size()-1;
while(l<r)
{
int sum=nums[i]+nums[l]+nums[r];
if(abs(sum-target)<abs(mind-target))
mind=sum;
if(sum<target)
++l;
else if(sum>target)
--r;
else return sum;
}
}
return mind;
}
};
和上一题相比,更加简单,没有了去重复操作,在while()
循环里只需要不断的更新最小的距离即可;
3.leetcode 18 四数之和
class Solution {
public:
vector<vector<int>> fourSum(vector<int>& nums, int target) {
if(nums.size()<4)return {};
vector<vector<int>>res;
sort(nums.begin(),nums.end());
for(int i=0;i<nums.size()-3;++i)
{
if(i>0&&nums[i-1]==nums[i])continue;
for(int j=i+1;j<nums.size()-1;++j)
{
if(j>i+1&&nums[j-1]==nums[j])continue;
int l=j+1,r=nums.size()-1;
while(l<r)
{
auto sum=nums[i]+nums[j]+nums[l]+nums[r];
if(sum==target)
{
res.push_back({nums[i],nums[j],nums[l],nums[r]});
while(l<r&&nums[l]==nums[l+1])
++l;
while(l<r&&nums[r]==nums[r-1])
--r;
++l;
--r;
}
else if(sum>target)
--r;
else
++l;
}
}
}
return res;
}
};
与三数之和最大的区别是它增加了一层循环,此外没有任何变化,尤其是while()
循环里核心代码一模一样;
4. 较小的三数之和
class Solution {
public:
int threeSumSmaller(vector<int>& nums, int target) {
if(nums.size()<3)return 0;
sort(nums.begin(),nums.end());
int cnt=0;
for(int i=0;i<nums.size()-2;++i)
{
int l=i+1,r=nums.size()-1;
while(l<r)
{
int sum=nums[i]+nums[l]+nums[r];
if(sum<target)
{
cnt+=(r-l);
++l;
}
else
--r;
}
}
return cnt;
}
};
总结:对于有序数组相关的问题,双指针是一个比较重要的技巧;当然上面的题目也可以用哈希表或者红黑树来做,但是时间复杂度方面没有提升,还增加了空间复杂度,所以不算是最优的解法。