双指针+排序+去重 15. 三数之和 16. 最接近的三数之和+18. 四数之和

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:找第一个数,第一个数相同的后续遍历都跳掉;(第一次遍历就找全了)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值