16. 最接近的三数之和&18.四数之和

16.题目描述:

给你一个长度为 n 的整数数组 nums 和 一个目标值 target。请你从 nums 中选出三个整数,使它们的和与 target 最接近。

返回这三个数的和。

假定每组输入只存在恰好一个解。

测试示例:

示例 1:

输入:nums = [-1,2,1,-4], target = 1
输出:2
解释:与 target 最接近的和是 2 (-1 + 2 + 1 = 2) 。


示例 2:

输入:nums = [0,0,0], target = 1
输出:0

提示:

3 <= nums.length <= 1000
-1000 <= nums[i] <= 1000
-104 <= target <= 104

解法一:暴力三重循环(会超时)

从头开始遍历数组,确定第一个数 nums[i] ,从 i 的下一位开始遍历 j ,从 j 的下一位开始遍历 k,对于每个 nums[i]+nums[j]+nums[k] 的组合,与 target 比较,计算出他们在数轴上的距离,并与最小值 minn 比较,如果更小就更新 minn 并记录当前组合的和。

三层循环,每层循环遍历N,时间复杂度为O(N^3)。

代码:

class Solution {
public:
    int threeSumClosest(vector<int>& nums, int target) {
        int ans;//记录三个数的和
        int length = nums.size();
        int x,y,z;
        int t=0;
        int minn=500000;
        for(int i=0;i<=length-3;i++)
        {
            x=nums[i];
            for(int j=i+1;j<=length-2;j++)
            {
                y=nums[j];
                for(int k=j+1;k<=length-1;k++)
                {
                    z=nums[k];
                    if(x+y+z>target)  //计算数轴上的距离
                    t=x+y+z-target;
                    else
                    t=target-x-y-z;
                    if(t<minn)   //更新最优解
                    {
                        minn=t;
                        ans = x+y+z;
                    }
                }
            }
        }
        return ans;
    }
};

解法二:排序+双指针

对于一个有序的数组,当我们确定第 i 位(0<=i<length-2)时,在(i,length-1]的范围内讨论剩余的两个数的位置。

初始时令左指针 left = i+1 ,右指针 right = n-1

对于当前的和 now = nums[i]+nums[left]+nums[right] ,比较它到 target 的差的绝对值与已记录的最优解 anstarget 的差的绝对值的大小,并根据结果决定是否更新最优解。

left < right 时,不断移动左右指针,当前和 now 与 target 的关系有以下三种情况:

  1. now == target
    此时不会有跟接近 target 的解了,返回答案 target
  2. now < target
    此时再减小 now 的大小一定不会得到更优解,所以右指针不动,左指针右移。
  3. now > target
    此时再增大 now 的大小一定不会得到更优解,所以左指针不动,右指针左移。
Other tips:

每当我们增加 i 时,可以判断 nums[i] 是否与上一次的值相同,如果相同,可以直接再增加 i 直到不同,因为相同的 i 值最优解相同。

同理,在移动左右指针时,我们也可以跳过相同的值。

class Solution {
public:
    int threeSumClosest(vector<int>& nums, int target) {
        sort(nums.begin(), nums.end());
        int length = nums.size();
        int ans = 999999999;  //记录最优解
        int now = 0;  //当前的和

        for(int i=0;i<length-2;i++)
        {
            if( i>0 && nums[i-1]==nums[i]) continue;  //跳过相同的i值
            int left = i+1;
            int right = length-1;
            while(left < right)
            {
                now = nums[i]+nums[left]+nums[right];
                if(now == target) return target;
                if(abs(target-now)<abs(target-ans))   //判断当前的解是否更优
                {
                   ans = now;
                }
                if(now > target)
                {
                    int right1 = right-1;
                    while( left < right1 && nums[right] == nums[right1-1]) right1--;
                    right = right1;
                }
                else
                {
                    int left1 = left+1;
                    while( left1 < right && nums[left] == nums[left1+1]) left1++;
                    left = left1;      
                }
            }
        }
        return ans;
    }
};
一些坑:
if( i>0 && nums[i-1]==nums[i]) continue;  //跳过相同的i值

这里不能使用while循环,否则会陷入死循环。

18.题目描述:

给你一个由 n 个整数组成的数组 nums ,和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]] (若两个四元组元素一一对应,则认为两个四元组重复):

  • 0 <= a, b, c, d < n
  • abc 和 d 互不相同
  • nums[a] + nums[b] + nums[c] + nums[d] == target

你可以按 任意顺序 返回答案 。

测试示例:

示例 1:

输入:nums = [1,0,-1,0,-2,2], target = 0
输出:[[-2,-1,1,2],[-2,0,0,2],[-1,0,0,1]]

示例 2:

输入:nums = [2,2,2,2,2], target = 8
输出:[[2,2,2,2]]

提示:

  • 1 <= nums.length <= 200
  • -109 <= nums[i] <= 109
  • -109 <= target <= 109

解法:排序+双指针

与前题不同的是,此题要确定四个数的和,那么我们要先循环遍历确定两个数的值,然后用双指针的方法来得到另外两个数相加之后的值

代码:

class Solution {
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) {
        vector<vector<int>> ans;
        int a,b,c,d;
        int length = nums.size();
        sort(nums.begin(), nums.end());
        for(int i=0;i<=length-4;i++)
        {
            a=nums[i];
            if(a>ceil(target/4)) break;
            for(int j=i+1;j<=length-3;j++)
            {
                b=nums[j];
                int left = j+1;
                int right = length-1;
                while(left<right)
                {
                    c=nums[left];
                    d=nums[right];
                    long long sum = 0ll+a+b+c+d;
                    if(sum == target)
                    {
                        ans.push_back({a,b,c,d});
                        int right1=right-1;
                        while(right1 > left && nums[right1]==nums[right]) right1--;
                        right = right1;
                        int left1=left+1;
                        while(left1 < right && nums[left1]==nums[left]) left1++;
                        left=left1;
                    }
                    else
                    {
                        if((long) a+b+c+d > target)
                        {
                            int right1=right-1;
                            while(right1 > left && nums[right1]==nums[right]) right1--;
                            right = right1;
                        }
                        if((long) a+b+c+d < target)
                        {
                            int left1=left+1;
                            while(left1 < right && nums[left1]==nums[left]) left1++;
                            left=left1;
                        }
                    }
                }
                while(j<=length-3 && nums[j+1]==nums[j]) j++;
            }
            while(i<=length-4 && nums[i+1]==nums[i]) i++;
        }
        return ans;

    }
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值