开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第23天,点击查看活动详情
两数之和系列
- 针对的都是有序数组,如果给定的数组是无序的,那么先排序。
- 两数之和系类都是双指针的使用,一个指向开头,一个指向结尾。
- 根据
nums[L] + nums[R]与target的大小来判断是++L还是--R。
两数之和II
只有一对答案,因此只需要一前一后靠近。 数组已经排序这点很重要
nums[L] + nums[R] < target:小于target,++L来增大区间和nums[L] + nums[R] > target:大于target,--R减少区间和nums[L] + nums[R] == target:可以直接得到结果返回
class Solution {
public:
std::vector<int> twoSum(const std::vector<int>& nums, int target) {
int L=0, R =nums.size()-1;
std::vector<int> result(2);
while(L < R) {
if(nums[L] + nums[R] == target)
{
result[0] = L+1;
result[1] = R+1;
return result;
}
else if(nums[L] + nums[R] < target)
{
++L;
}
else
{
--R;
}
}
return result;
}
};
最接近的三数之和
对于nums中每个数,从其位置右边开始选出来两个数,使得三数之和最接近target。于是使用三个指针:cur,L=cur+1,R=length-1。需要先对数组进行排序,根据三个指针的和,来调整指针位置。
- 如果
sum = nums[cur] + nums[L] +nums[R] > target,需要--R来缩小, - 如果
sum = nums[cur] + nums[L] +nums[R] < target,需要++L来扩大, - 如果
sum == target找到答案,直接返回。
class Solution {
public:
int threeSumClosest(std::vector<int>& nums, int target) {
std::sort(nums.begin(), nums.end());
int64_t result = INT_MAX;
for(int cur=0, length = nums.size(); cur < length; ++cur) {
for(int L =cur+1, R =length-1; L < R;) {
int64_t sum = nums[cur] + nums[L] + nums[R];
if(std::abs(target - sum) < std::abs(target - result))
{
result = sum;
}
// 更新指针
if(sum < target)
{
++L;
}
else if(sum > target)
{
--R;
}
else
{
// sum == target
return static_cast<int>(result);
}
} // inside-for -end
} // outside-for -end
return static_cast<int>(result);
}
};
这个问题的解法思想,类似于盛水最多的容器,或者说是一种滑动窗口的思想:移动左指针变大,移动右指针变小。
三数之和
这道题和上一道几乎异曲同工。只是这道题是找出所有不重复的答案,因此在每次找到答案后需要剔除重复的元素。原理和上题一模一样。
class Solution {
public:
std::vector<std::vector<int>> threeSum(std::vector<int>& nums) {
std::vector<std::vector<int>> result;
if(nums.size() <3)
return result;
std::sort(nums.begin(), nums.end());
for(int curr =0, numsLen = nums.size(); curr < numsLen; ) {
for(int L=curr+1, R =numsLen-1; L <R; ) {
int sum = nums[curr] + nums[L] + nums[R];
if(sum < 0)
{
++L;
}
else if(sum >0)
{
--R;
}
else
{
result.emplace_back<std::vector<int>>({nums[curr], nums[L], nums[R]});
++L;
while(L < R && nums[L-1] == nums[L]) ++L; // 为了剔除重复元素
--R;
while(L < R && nums[R] == nums[R+1]) --R; // 为了剔除重复元素
}
}
++curr;
while(curr < numsLen && nums[curr-1] == nums[curr]) ++curr; // 为了剔除重复元素
}
return result;
}
};
四数之和
最直接的想法就是在 三数之和 外面再套一层循环。
class Solution {
public:
vector<vector<int>> fourSum(vector<int>& nums, int target) {
std:vector<std::vector<int>> result;
if(nums.size() < 4)
return result;
std::sort(nums.begin(), nums.end());
for(int curr=0, numsLen =nums.size(); curr < numsLen; ) {
M_threeSum(nums, curr, target-nums[curr], result);
++curr;
while(curr < numsLen && nums[curr-1] == nums[curr]) ++curr; // 为了剔除重复元素
}
return result;
}
private:
void M_threeSum(const std::vector<int>& nums,
int start,
int target,
std::vector<std::vector<int>>& result) {
for(int curr =start+1, numsLen = nums.size(); curr < numsLen; ) {
for(int L=curr+1, R =numsLen-1; L <R; ) {
int sum = nums[curr] + nums[L] + nums[R];
if(sum < target)
{
++L;
}
else if(sum > target)
{
--R;
}
else
{
result.emplace_back<std::vector<int>>({nums[start],
nums[curr],
nums[L],
nums[R]});
++L;
while(L < R && nums[L-1] == nums[L]) ++L; // 为了剔除重复元素
--R;
while(L < R && nums[R] == nums[R+1]) --R; // 为了剔除重复元素
}
}
++curr;
while(curr < numsLen && nums[curr-1] == nums[curr]) ++curr; // 为了剔除重复元素
}
}
};
至此,两数之和系列已经全数解决,都是一个双指针模板。