15. 三数之和
给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有满足条件且不重复的三元组。
注意:答案中不可以包含重复的三元组。
示例:
给定数组
nums = [-1, 0, 1, 2, -1, -4],
满足要求的三元组集合为:
[
[-1, 0, 1],
[-1, -1, 2]
]
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/3sum
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
解题
求三数之和,先排序数组;
若给出的为空数组,返回空数组;
从头遍历数组,取i,head=i+1,tail=N-1(最后元素),
1.若i>0则无需判断,三个数肯定大于0;
2.若i+head+tail>0,左移tail;
3.若i+head+tail<0,右移head;
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
sort(nums.begin(),nums.end());
int head;
int tail;
int sum;
vector<vector<int>> result;
vector<int> temp;
if (nums.size()==0) return result; //空数组
for(int i=0;i<nums.size();i++)
{
if(nums[i]>0) break; //遇到大于0的直接停止//后面都是大于0的
head = i+1;
tail =nums.size()-1;
while(head<tail)
{
sum = nums[i]+nums[head]+nums[tail];
if(sum==0)
{
temp.clear();
temp.push_back(nums[i]);
temp.push_back(nums[head]);
temp.push_back(nums[tail]);
result.push_back(temp);
tail--;
head++;
}
if(sum<0) head++;
if(sum>0) tail--;
}
}
return result;
}
};
难点:去掉重复的三个数
上述代码给出的答案会有所有的重复元素;
如何去重?
(1)跳过多个 i 相同元素的重复解
//去重,只操作重复的第一个元素
if(i>0&&nums[i]==nums[i-1]) continue;
因为nums已排序,故重复的元素连续出现,只使用重复元素的第一个元素便可以得到用到该元素的所有的解(一定要第一个,因为可能用到后面的重复元素);
(2)跳过同一个 i 的重复解
while(head<tail&&nums[head]==nums[head+1]) ++head;
while(head<tail&&nums[tail]==nums[tail-1]) --tail;
当找到了一个sum==0的head和tail时,若后面有多个重复的head和tail,需要跳过他们,为防止数组越接,需要条件head<tail;
(所有元素都相同的边界条件)
完整代码
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
sort(nums.begin(),nums.end());
int head;
int tail;
int sum;
vector<vector<int>> result;
vector<int> temp;
if (nums.size()==0) return result; //空数组
for(int i=0;i<nums.size();i++)
{
if(nums[i]>0) break; //遇到大于0的直接停止//后面都是大于0的
//去重,只操作重复的第一个元素
if(i>0&&nums[i]==nums[i-1]) continue;
head = i+1;
tail =nums.size()-1;
while(head<tail)
{
sum = nums[i]+nums[head]+nums[tail];
if(sum==0)
{
temp.clear();
temp.push_back(nums[i]);
temp.push_back(nums[head]);
temp.push_back(nums[tail]);
result.push_back(temp);
//-2 0 2
//0 0 0 head<tail
while(head<tail&&nums[head]==nums[head+1]) ++head;
while(head<tail&&nums[tail]==nums[tail-1]) --tail;
tail--;
head++;
}
if(sum<0) head++;
if(sum>0) tail--;
}
}
return result;
}
};
16. 最接近的三数之和
给定一个包括 n 个整数的数组 nums 和 一个目标值 target。找出 nums 中的三个整数,使得它们的和与 target 最接近。返回这三个数的和。假定每组输入只存在唯一答案。
例如,给定数组 nums = [-1,2,1,-4], 和 target = 1.
与 target 最接近的三个数的和为 2
. (-1 + 2 + 1 = 2).
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/3sum-closest
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
解题
与上题类似,遍历nums[i],设定head和tail;
对于每个数利用双指针遍历一遍数组,故总复杂度为O(N^2);
class Solution {
public:
int threeSumClosest(vector<int>& nums, int target) {
sort(nums.begin(),nums.end());
int head=0;
int tail=nums.size()-1;
int Mindis=abs(nums[0]+nums[1]+nums[2]-target);
int result=nums[0]+nums[1]+nums[2];
int tmp;
for(int i=1;i<nums.size()-1;i++)
{
head=0;
tail=nums.size()-1;
while(head<i&&tail>i)
{
tmp=abs(nums[tail]+nums[head]+nums[i]-target);
if(tmp<Mindis)
{
Mindis=tmp;
result=nums[tail]+nums[head]+nums[i];
}
if(nums[tail]+nums[head]+nums[i]>target) tail--;
else if(nums[tail]+nums[head]+nums[i]<target) head++;
else return target;
}
}
return result;
}
};
注意点
因为求最接近的值,所以每一个点都要完全遍历,除非找到完全相同的值,其他情况不能提前退出循环;
18. 四数之和
给定一个包含 n 个整数的数组 nums 和一个目标值 target,判断 nums 中是否存在四个元素 a,b,c 和 d ,使得 a + b + c + d 的值与 target 相等?找出所有满足条件且不重复的四元组。
注意:
答案中不可以包含重复的四元组。
示例:
给定数组 nums = [1, 0, -1, 0, -2, 2],和 target = 0。
满足要求的四元组集合为:
[
[-1, 0, 0, 1],
[-2, -1, 1, 2],
[-2, 0, 0, 2]
]
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/4sum
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
解题
四数之和结合两道三数之和,需要 存放找到的集合+去重;
四数之和就是在三数基础上再多一轮循环,复杂度为O(N^3);
class Solution {
public:
vector<vector<int>> fourSum(vector<int>& nums, int target) {
sort(nums.begin(),nums.end());
vector<vector<int>> result; //存放总结果
if (nums.size()<4) return result;
int head;
int tail;
int sum;
for(int i = 0;i<nums.size();i++)
{
if(i>0&&nums[i]==nums[i-1]) continue;
for(int j=i+1;j<nums.size();j++)
{
if(j>i+1&&nums[j]==nums[j-1]) continue;
head=j+1;
tail=nums.size()-1;
while(head<tail)
{
sum=nums[i]+nums[j]+nums[head]+nums[tail];
if(sum==target)
{
result.push_back({nums[i],nums[j],nums[head],nums[tail]});
//去掉重复元素
while(head<tail&&nums[head]==nums[head+1]) head++;
while(head<tail&&nums[tail]==nums[tail-1]) tail--;
head++;tail--;
}
else if(sum<target) head++;
else if(sum>target) tail--;
}
}
}
return result;
}
};
难点:去重
每层遍历需要去重;
1:(找第三第四个数)当找到sum时,推进head和tail,直到找到下一个不同的head和tail;(相同的head必定要相同的tail匹配)
2:找第二个数,第二个数相同的后续遍历都跳掉;(第一次遍历就找全了)
if(j>i+1&&nums[j]==nums[j-1]) continue;
3:找第一个数,第一个数相同的后续遍历都跳掉;(第一次遍历就找全了)