优选算法精品课--双指针算法(2)

1、有效三角形的个数

题目链接:
https://leetcode.cn/problems/valid-triangle-number/

1.1 题目解析

在这里插入图片描述
题目的意思就是让我们选出所有能组合为一个三角形的三个数的组合,并且如图两个二算两种。

1.2 思路解析

首先三角形成立的条件是两边之和大于第三边,两边之差小于第三边,但是如果我们按这个条件
来判断成立三角形的话可以倒是可以,但是太麻烦了,现在我们有一种新的思路:
假设:2 3 4是三角形三条边,那我们是不是可以只需要判断2+3>4即可,为什么呢?
因为2+4一定大于3,3+4一定大于2,3-2一定小于4、
当2+3>4成立时一定有4-3<2,4-2<3.

所以第一步我们需要将数组排序(这样更好找最大最小值)

在这里插入图片描述
然后我们固定第三条边和第一条边,我们通过改变第二条边来判断该组合是否成立:
这里有两种情况:
1、arr[o]+arr[t]>arr[tr],在这种情况下,arr[o]后面的所有值与arr[t]相加都大于arr[tr];
所以这种情况只需要计算区间的大小,区间的大小就是组合的数量

2、arr[o]+arr[t]<=arr[tr],这种情况下,无论我们如何移动左区间,和都小于arr[tr],所以我们现在要移动右区间,直到arr[o]+arr[t]>arr[tr];

大家下来可以画画图来理解一下.

总结:
在这里插入图片描述

1.3 代码实现

class Solution {
    
    
public:
    int triangleNumber(vector<int>& nums) {
    
    

        sort(nums.begin(),nums.end());//算法库里的排序
        int tail=nums.size()-1;//最大边
        int count=0;//计数
        while(tail>=2)//第三条边必须要在第三个数组元素及以后
        {
    
    
            int left=0;//最小边
            int right=tail-1;
            while(left<right)
            {
    
    
                if(nums[left]+nums[right]>nums[tail])
                {
    
    
                    count+=right-left;
                    right--;
                }
                else
                {
    
    
                    left++;
                }
            }
            tail--;
        }

        return count;
    }
};

2、和为s的两个数

题目链接:
https://leetcode.cn/problems/he-wei-sde-liang-ge-shu-zi-lcof/

2.1 题目解析

在这里插入图片描述
这道题的意思就是需要我们在数组中找两个数,它们的和为target,只需要返回任意一组即可。

2.2 思路解析

这道题是不是类似于第一题啊,这一题我们的思路是:
在这里插入图片描述

假设这是我们的数组,我们还是需要先排序,然后计算arr[left]+arr[right]的和,在与target比较即可

三种情况:
‘<‘,这种情况我们直接left++即可
’>’,这种情况我们需要让right–;
‘==’,这组值就是我们需要的。

总结:
在这里插入图片描述

2.3 代码实现

class Solution {
    
    
public:
    vector<int> twoSum(vector<int>& price, int target) {
    
    
        int left=0,right=price.size()-1;
        vector<int> v;
        
        while(left<right)
        {
    
    
            if(price[left]+price[right]<target) left++;
            else if(price[left]+price[right]>target) right--;
            else
            {
    
    
                v.push_back({
    
    price[left],price[right]});
                break;
            }
        }

        return v;
    }
};

3、三数之和

题目链接
https://leetcode.cn/problems/3sum/

3.1 题目解析

在这里插入图片描述
这道题难度就上来了,需要我们找出三个和为0的值,并且需要我们将相同组合给去掉(元素相同,顺序不同算同一组合)。

3.2 思路解析

解法一:暴力遍历,求出所有组合,通过容器set去重(太麻烦了)

解法二:双指针算法

现在这里的思路是先固定左边的一个数,这个数右边的部分作为一个区间,将左边这个数的相反数作为target,在右边的区间求两数之和等于target,这样我们的问题就变为求两数之和了。

在这里插入图片描述
重复上述操作,但是这道题难的部分是去重(去掉相同组合)。

去重需要注意的是这里由两部分需要去重:
第一部分是找到当前组合后left需要++,right需要- -,要是遇到相同元素需要跳过
第二部分是最左边的target遇到相同元素也需要去重

总结:
在这里插入图片描述

3.3 代码实现

class Solution {
    
    
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
    
    
        
        vector<vector<int>>  vv;
        sort(nums.begin(),nums.end());

        int n=nums.size();
        for(int i=0;i<n;)
        {
    
    
            int left=i+1,right=n-1,target=-nums[i];

            while(left<right)
            {
    
    
                if(nums[left]+nums[right]<target) left++;
                else if(nums[left]+nums[right]>target) right--;
                else
                {
    
    
                    vv.push_back({
    
    nums[left],nums[right],nums[i]});
                    left++;
                    right--;
 
                    while(left<right&&nums[left]==nums[left-1]) left++;//遇到重复的
                    while(left<right&&nums[right]==nums[right+1]) right--;//遇到重复的
                }

            }

            i++;
            while(i<n&&nums[i]==nums[i-1]) i++;//去重操作
        }

        return vv;
    }
};

4、四数之和

题目链接
https://leetcode.cn/problems/4sum/

4.1 题目解析

在这里插入图片描述
这道题与上面的三数求和十分类似,需要我们求四个数的和。

4.2 思路解析

上一题三数求和是在左边固定一个数,然后在右区间找两数,两数等于左边那个数的相反数,
这道题需要我们找四个数,那我们可以在左边固定两个数,然后右边的部分作为右区间,需要target-左边两个数=右边两数之和,完全类似,但是这道题需要注意的是int可能会溢出,我们需要使用long long,
其次三数求和是两个循环,需要注意两个地方的去重
而这里是三个循环,需要注意三个地方的去重:
1、left与right部分
2、左边两数中右边那数的去重
3、左边两数中最左边那个数的去重

总结
在这里插入图片描述

4.3 代码实现

class Solution {
    
    
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) {
    
    
        vector<vector<int>>  vv;
        sort(nums.begin(),nums.end());

        int n=nums.size();
        for(int i=0;i<=n-4;)
        {
    
    
            for(int j=i+1;j<=n-3;)
            {
    
    
                int left=j+1,right=n-1;
                long long aim=(long long)target-nums[i]-nums[j];
                while(left<right)
                {
    
    
                    
                    if(nums[left]+nums[right]<aim) left++;
                    else if(nums[left]+nums[right]>aim) right--;
                    else
                    {
    
    
                        vv.push_back({
    
    nums[i],nums[j],nums[left],nums[right]});
                        left++,right--;

                        while(left<right&&nums[left]==nums[left-1]) left++;
                        while(left<right&&nums[right]==nums[right+1]) right--;
                    }
                }

                j++;
                while(j<n&&nums[j-1]==nums[j]) j++;
            }
        
        i++;
        while(i<n&&nums[i-1]==nums[i]) i++;
        }

        return vv;
    }
};

5 总结

1、双指针常用于数组中元素的操作,但其他地方例如快乐数也可以使用,总的来说看到数组我们可以优先考虑双指针是否可行,然后考虑其他算法

2、在数组中对元素操作我们尽量都要画图,一定要考虑特殊情况,否则会把我们坑得死死的。

猜你喜欢

转载自blog.csdn.net/2401_87257864/article/details/143302883