双指针算法·两数之和·三数之和

如题目所示,这篇文章要讲解的三道题是有很大的关联的,循循渐进逐步展开,介绍双指针算法在解决这类题时能带来的优化。
两数之和
在这里插入图片描述
这道题比较简单,也是带入这种题型不错的选择,如题目所说,提供的数组已经是升序,使用暴力解法将所有的两数组合全部遍历一遍,这种解法的时间复杂度为O(N2),而且题目所提供的升序数组也没有用到,素以我们可以借助升序来寻找跟好的算法。
双指针你一定会想到,因为是升序,我们可以用两个指针left和right分别指向数组首位置和末位置,这样就可以知道最大元素和最小元素的和,如果和是大于目标值,就左移right,此时得到的目标值就会减小(因为是升序嘛),如果小于目标值,那就右移left,这样的道德目标值就会变大,这样只需要遍历一遍就可以债哦哦到结果。
以下是C++解法

vector<int> twoSum(vector<int>& price, int target) {
	int i=0,j=price.size()-1;
    vector<int> a;//偷懒了i->left j->right
    while(i!=j)
    {
        int sum=price[i]+price[j];
        if(sum<target)
        {
            i++;
        }
        if(sum>target)
        {
            j--;
        }
        if(sum==target)
        {
            break;
        }
    }
    a.push_back(price[i]);
    a.push_back(price[j]);
    return a;
}

三数之和
在这里插入图片描述

有了前一道题的衬托,我们就可以很轻松的想出这道题的解法,首先还是要介绍暴力解法,三层for循环,遍历出所有符合条件的三元组,但是这道题有很难搞的地方就是不可以包含重复的三元组,所以我们找出所有的三元组之后还需要进行去重操作。
先思考思考如何使用双指针算法思想进行解决,对于第一道题目,有着很重要的条件,那就是升序数组,我们可以找出规律从而简化以减少遍历次数,减小时间复杂度。
所以我们要先将数组排序,返回值类型为二维数组,我们提前构造一个二维数组作为返回值。
在这里插入图片描述
此时数组已经是一个升序数组了,我们的目标是找到三个数是他们的和等于0,换一种思路就是找到两个数,使他们的和等于第三个数的负数,这样就回归到两数之和了,首先我们要先锁定一个数,作为第三个数,在剩下的数中找到两个使它们的和等于锁定的数的负数即可,找两个数的操作和第一题的思路一模一样。
在这里插入图片描述

如何去重呢?
这是一个比较麻烦的问题。
在找到一组符合要求的值之后,判断left后边的和前边的值是否和已经记录过的相同,如果相同的话,那就跳过相同的数,就可以实现去重操作。不要忘记咯,我们选择的目标值也是要跳过相同的元素(就是目标值也是不同的),这样就切切实实实现了去重操作。
举例说明:
在这里插入图片描述
在锁定1之后,后边的1也要全部跳过,在left为-1right为0时,符合条件,但是为了去重,right要跳过所有的1,left要跳过所有的-1,即去重操作。要注意的是,在left前移和right后移的过程中,left保持恒小于right。
这道题目还要注意的点就是返回的对象为二维数组,在打印时可以使用两层for循环。
代码如下

class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums)
{
    vector<vector<int>> a;
    sort(nums.begin(), nums.end());
    int size = nums.size() - 1;
    for (int i = size; i >= 0; )
    {
        if (nums[i] < 0)
        {
            break;
        }
        int target = -nums[i];
        int right = i - 1;
        int left = 0;
        while (left < right)
        {
            if (nums[right] + nums[left] > target)right--;
            else if (nums[right] + nums[left] < target)left++;
            else {//此时我们找到了符合条件的值
                a.push_back({ nums[i],nums[right],nums[left] });//直接将这个三元组插入
                left++;
                right--;
                //去重操作
                while (left < right && nums[left] == nums[left - 1])left++;
                while (left < right && nums[right] == nums[right + 1])right--;
            }
        }
        i--;
        while (i > 0 && nums[i] == nums[i + 1])
        {
            i--;
        }
    }
    for (auto i : a)
    {
        for (auto s : i)
        {
            cout << s << ' ';
        }
        cout << endl;
    }
    return a;
}
};
  • 23
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

we will rise.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值