利用指针找最大值_利用双指针解Leetcode第16题:最接近的三数之和

题目描述

给定一个包括 n 个整数的数组 nums 和 一个目标值 target。找出 nums 中的三个整数,使得它们的和与 target 最接近。返回这三个数的和。假定每组输入只存在唯一答案。

例如,给定数组 nums = [-1,2,1,-4], 和 target = 1.
与 target 最接近的三个数的和为 2. (-1 + 2 + 1 = 2).

解题思路与代码

双指针法的解题思路:

  1. 先让数组有序,也就是需要先对数组进行排序
  2. 然后每次固定一个元素,再去寻找另外两个元素,也就是双指针

双指针法的代码实现:

  1. 利用 sort() 对数组进行排序。
  2. 初始化一个用于保存结果的值 res = nusm[0] + nums[1] + nums[2] (不要自己设初值,直接从数组中抽取三个元素,假设这是最接近的三数之和,然后再更新就是了)。
  3. 利用下标 i 对数组进行遍历,此时就是在固定第一个元素,注意,下标 i 的边界为 i < nums.length-2,否则设置指针的时候会出现数组越界。
  • 每次遍历的过程中设置两个指针,分别是 left = i + 1、right = nums.length - 1。
  • 检查 sum = nums[i] + nums[left] + nums[right]与 target 的距离,如果该距离比之前保存的 res 与 target 的距离更小,就更新 res。
  • 然后就是移动双指针:如果 sum 的值比 target 大,那么我们让 right--,因为数组是有序的,right --会使得下一次的 sum 更小,也就更接近 target 的值;同理,如果 sum 的值 target 小,那么我们让 left++。·
  • left++ 和 right-- 的界限自然是 left <right,如果 left == right,说明我们已经将所有的元素都遍历过一遍了。

重复上面的操作,直到i循环结束为止,返回 res。

代码实现:

class Solution {
public:
    int threeSumClosest(vector<int>& nums, int target) {
        int res=nums[0]+nums[1]+nums[2];//初始化,不能自己设置成无限大或者无限小
        sort(nums.begin(),nums.end());//排序
        
        for(int i=0;i<nums.size()-2;i++)
        {
            int left=i+1,right=nums.size()-1;
            while(left<right)
            {
                int sum=nums[i]+nums[left]+nums[right];
                if(abs(sum-target)<abs(res-target))//更新res
                    res=sum;
                if(sum>target)
                {
                    right--;
                }
                else
                {
                    left++;
                }   
            }
        }
        return res;
    }
};

提交结果:

a448df595ff8cd4778b80c3f161b0064.png

算法优化

实际上,受到

驭风者:利用双指针解Leetcode第15题:三数之和​zhuanlan.zhihu.com
zhihu-card-default.svg

题目中不重复部分处理方法的启发,我们前面的算法可以进行三方面的优化。

  1. 元素重复的问题

举个例子,nums = [1,1,1,2,3] ,target = 7,那么最终的结果应该是 6 (1 + 2 + 3)。但是按照上面的代码,在遍历的时候 nums[i]会重复的等于 1 这个数,但是其实之前 nums[i] 等于 1 已经遍历过了,后面的遍历都属于无用的遍历。所以可以添加去重的操作。

2. 超越界限的问题

举个例子,nums = [-3,-1,3,4,5]。

  • 假设 i = 0,left = 1,right = 4,那么每次 left 和 right 之间都有许多元素,那么 left 和 right 之间的元素之和肯定也有一个最小值和一个最大值。
  • 就如同 left = 1,right = 4,那么移动指针的情况下,nums[left] + nums[right] 的最小值肯定为 nums[left] + nums[left + 1],因为这两个元素是 left 和 right 范围内能取到的最小的两个元素,同理可证最大值。
  • 如果 target 的值比 nums[i] + nums[left] + nums[left + 1] 的值还小,那么双指针无论怎么取,最后都会取到 nums[i] + nums[left] + nums[left + 1];同理可证 target 的值比nums[i] + nums[right] + nums[right - 1] 的值还大的情况。
  • 所以可以增加一个判断,满足条件的情况下就可以直接取值,而不需要双指针一步步的判断来进行取值,减少了双指针的移动。

3. 三数之和等于 target 的问题

举个例子,nums = [1,1,2,3,4,5,6,10] ,target = 12,那么最终的结果应该是 12 (1 + 1 + 10)。

有些时候,可能会直接找到三数之和等于 target 的情况,此时直接返回结果即可,不需要在进行之后的循环,因为不可能有数比他自己更接近自己了。

优化后的算法代码如下:

class Solution {
public:
    int threeSumClosest(vector<int>& nums, int target) {
        int res=nums[0]+nums[1]+nums[2];//初始化
        sort(nums.begin(),nums.end());//排序
        
        for(int i=0;i<nums.size()-2;i++)
        {
            int left=i+1,right=nums.size()-1;
            while(left<right)
            {
                //最小值>target情况
                int min=nums[i]+nums[left]+nums[left+1];
                if(target<min)
                {
                    if(abs(min-target)<abs(res-target))
                        res=min;
                    break;
                }
                //最大值<target情况
                int max=nums[i]+nums[right]+nums[right-1];
                if(target>max)
                {
                    if(abs(max-target)<abs(res-target))
                        res=max;
                    break;
                }
                  
                int sum=nums[i]+nums[left]+nums[right];
                //与target相等,直接返回
                if(sum==target)
                    return sum;
                
                if(abs(sum-target)<abs(res-target))
                    res=sum;//更新res
                
                //更新左右指针,并滤过相同的数字
                if(sum>target)
                {
                    while(left<right && nums[right]==nums[--right]) ;
                }
                else
                {
                    while(left<right && nums[left]==nums[++left]) ;
                }   
            }
        }
        return res;
    }
};

提交结果:

e1e3442d1298799a24fff0f9ad0c97e3.png
优化后执行效果大幅提升

参考:

驭风者:利用双指针解Leetcode第15题:三数之和​zhuanlan.zhihu.com
zhihu-card-default.svg
力扣​leetcode-cn.com
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值