力扣四数之和
一、题目描述
二、分析
这道题和:三数之和:三数之和类似,直接放代码:
三、代码
class Solution {
public:
// 只需要返回三个数的和,根据15的双指针来改
int threeSumClosest(vector<int>& nums, int target) {
int n = nums.size();
if( n < 3 )
return NULL;
if( n == 3 )
return nums[0]+nums[1] + nums[2];
// 仍然需要重排
sort( nums.begin(), nums.end() );
int minMerge = INT_MAX;
int ans = 0;
int tmpSum = 0;
for( int first = 0; first < n; first++ )
{
int second = first + 1;
int third = n - 1;
while( second < third )
{
tmpSum = nums[first] + nums[second] + nums[third];
// 如果当前距离小于目前最小距离,则需要更新
if( abs(tmpSum - target) < minMerge )
{
minMerge = abs( tmpSum - target );
ans = tmpSum;
}
// 然后决定下一步怎么走
if( tmpSum < target )
{
// 首先,tmpSum < target说明右侧可能有更好的结果,必须是像右侧走,second++
second++;
}
else if (tmpSum > target)
{
third--;
}
else
{
// 如果已经相等,那么间隔就是0
return ans;
}
}
}
return ans;
}
};
四、问题描述
五、分析
原理和三数之和完全一样
六、代码
class Solution{
public:
vector<vector<int>> fourSum(vector<int>& nums, int target) {
sort(nums.begin(),nums.end());
vector<vector<int> > res;
if(nums.size() < 4)
return res;
int a,b,c,d,_size=nums.size();
for(a = 0;a <= _size - 4;a++)
{
if(a > 0 && nums[a] == nums[a - 1])
continue; //确保nums[a] 改变了
for(b = a + 1;b <= _size - 3;b++)
{
if(b > a + 1 && nums[b] == nums[b - 1])
continue; //确保nums[b] 改变了
c = b + 1,d = _size - 1;
while(c < d)
{
if(nums[a] + nums[b] + nums[c] + nums[d] < target)
c++;
else if(nums[a] + nums[b] + nums[c] + nums[d] > target)
d--;
else
{
res.push_back({nums[a],nums[b],nums[c],nums[d]});
while(c < d && nums[c + 1] == nums[c]) //确保nums[c] 改变了
c++;
while(c <d && nums[ d - 1] == nums[d]) //确保nums[d] 改变了
d--;
c++;
d--;
}
}
}
}
return res;
}
};
七、100Sum 问题?
- 在 LeetCode 上,4Sum 就到头了,但是回想刚才写 3Sum 和 4Sum的过程,实际上是遵循相同的模式的。我相信你只要稍微修改一下 4Sum 的函数就可以复用并解决 5Sum 问题,然后解决 6Sum问题……
- 那么,如果我让你求 100Sum 问题,怎么办呢?其实我们可以观察上面这些解法,统一出一个 nSum 函数:
/* 注意:调用这个函数之前一定要先给 nums 排序 */
vector<vector<int>> nSumTarget(
vector<int>& nums, int n, int start, int target) {
int sz = nums.size();
vector<vector<int>> res;
// 至少是 2Sum,且数组大小不应该小于 n
if (n < 2 || sz < n)
return res;
// 2Sum 是 base case
if (n == 2)
{
// 双指针那一套操作
int lo = start, hi = sz - 1;
while (lo < hi)
{
int sum = nums[lo] + nums[hi];
int left = nums[lo], right = nums[hi];
if (sum < target)
{
//去掉重复的结果
while (lo < hi && nums[lo] == left)
lo++;
}
else if (sum > target)
{
//去掉重复的结果
while (lo < hi && nums[hi] == right)
hi--;
}
else
{
res.push_back({left, right});
while (lo < hi && nums[lo] == left)
lo++;
while (lo < hi && nums[hi] == right)
hi--;
}
}
}
else
{
// n > 2 时,递归计算 (n-1)Sum 的结果
for (int i = start; i < sz; i++)
{
vector<vector<int>>
sub = nSumTarget(nums, n - 1, i + 1, target - nums[i]);
for (vector<int>& arr : sub)
{
// (n-1)Sum 加上 nums[i] 就是 nSum
arr.push_back(nums[i]);
res.push_back(arr);
}
while (i < sz - 1 && nums[i] == nums[i + 1])
i++;
}
}
return res;
}
- 嗯,看起来很长,实际上就是把之前的题目解法合并起来了,
n == 2 时是 twoSum 的双指针解法,n > 2时就是穷举第一个数字,然后递归调用计算 (n-1)Sum,组装答案
。 - 需要注意的是,调用这个 nSum 函数之前
一定要先给 nums 数组排序
,因为 nSum 是一个递归函数,如果在 nSum 函数里调用排序函数,那么每次递归都会进行没有必要的排序,效率会非常低。 - 比如说现在我们写 LeetCode 上的 4Sum 问题:
vector<vector<int>> fourSum(vector<int>& nums, int target) {
sort(nums.begin(), nums.end());
// n 为 4,从 nums[0] 开始计算和为 target 的四元组
return nSumTarget(nums, 4, 0, target);
}
- 再比如 LeetCode 的 3Sum 问题,找 target == 0 的三元组:
vector<vector<int>> threeSum(vector<int>& nums) {
sort(nums.begin(), nums.end());
// n 为 3,从 nums[0] 开始计算和为 0 的三元组
return nSumTarget(nums, 3, 0, 0);
}
那么,如果让你计算 100Sum 问题,直接调用这个函数就完事儿了。