双指针算法(二)

发布于:2025-04-18 ⋅ 阅读:(22) ⋅ 点赞:(0)

目录

一、力扣611——有效三角形的个数

二、牛客网3734——和为S的两个数字

三、力扣15——三数之和

四、力扣18——四数之和


一、力扣611——有效三角形的个数

题目如下:

 这里我们先认识如何判断是个三角形,a+b>c,a+c>b,b+c>a即为三角形

这里我们假设a<=b<=c,这里我们c是最大的,c加任何一个正数都比a或b大,所以我们只需要判断a+b是否>c就可以了。

所以我们这里算法思想是

  • 先对这个数组进行排序,然后固定最大数,定义left和right,left从0开始,right从第二大的数开始,判断left+right是否大于这个最大的数。
  • 如果left+right > 这个最大的数,那么说明left之后的数加上right所指向的数都大于这个固定的数。这个总共有right-left个个数符合这个条件。right再--,判断前一个加上left是否大于,直到left个right遍历完除去固定数的数组。

  • 如果left+right<这个最大的数,这个时候left++,再继续判断。

固定7后,再固定第二大的数,继续上面的算法逻辑就行。

代码如下:

class Solution {
public:
    int triangleNumber(vector<int>& nums) {
        int size = nums.size();
        sort(nums.begin(),nums.end());
        int n = size - 1;   //最开始固定数组下标的最大值
        int count = 0;
        while(n >= 2)
        {
            int left = 0;
            int right = n -1;
            while(right > left)
            {
                if(nums[left] + nums[right] <= nums[n])
                {
                    left++;
                    continue;
                }
                else
                {
                    int cnt = right - left;
                    count += cnt;
                    right--;
                }
            }
            n--;
        }
        return count;

    }
};

二、牛客网3734——和为S的两个数字

题目如下:

这里是一个有序数组,对于一个有序数组的操作,我们优先要有双指针和二分的思想,双指针效果更优,后面也会给大家讲解二分算法。

这里从数组找到两个数之和等于S,这里我们还是定义两个指针left和right分别指向数组的头和尾,这两个指针所指向的位置相加。

  • 如果array[left]+array[right] > S,我们让right--,因为这是一个升序的有序数组。right-1所指向的值是比right所指向的值要小的。 
  • 如果array[left]+array[right] < S,我么让left++,left+1所指向的值是比left指向的值要大的。
  • 根据left和right不断遍历这个数组,如果找到了array[left]+array[right] == S,返回left和right所指向的值,没找到返回一个空数组。

代码如下:

class Solution {
public:
    vector<int> FindNumbersWithSum(vector<int> array,int sum) {
        vector<int> v1;
        int left = 0;
        int right = array.size() - 1;
        while(left < right)
        {
            if(array[left] + array[right] < sum)
            {
                left++;
            }
            else if(array[left] + array[right] > sum)
            {
                right--;
            }
            else {
                v1.push_back(array[left]);
                v1.push_back(array[right]);
                return v1;
            }
        }
        return v1;
    }
    
};

三、力扣15——三数之和

题目如下:

给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != ji != k 且 j != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请你返回所有和为 0 且不重复的三元组。

这里还要注意不可以包含重复的三元组。 

其实这道题的解题思路是和有效三角形的个数是一样的。只是这道题要求不能重复。

所以我们就多了一个去重的操作。

这里满足的条件是 nums[i] + nums[j] + nums[k] == 0 ,说明要么这三个数全是0,要么就存在一个负数。我们先对这个数组进行排序,我们固定第一个数(0或负数),定义left为固定数+1,right为这个数组的尾部。

我们要满足三个数之和为0的话,这里我们固定了-4,所以我们只需要找到另外两个数相加等于4就行。跟上面找两数之和的思路一模一样,这里也不直接细讲了。

只是这里我们不是找到了就直接返回,而是要继续找,所以当找到这个符合条件left和right所指向位置的时候,left++,right--,缩小范围继续寻找。

固定完-4之后,固定-2依次往下,直到这个固定的数>0就结束了,这是一个小优化 ,因为如果这个数大于0的话,那么left和right都在这个数的右边,他们相加无论如何都不可能等于0,只会大于0.

算法思路就是如上,接下来我们讲如何去重

我们可能第一想法是用一个unordered_set容器,把符合条件的三元组全放到这个容器中,就能完成去重,那我有其他办法在遍历数组的方式上完成去重呢?

答案是有的

当我们是这种情况的时候,left,right所指向的数之和==4,left后一个也是0,这样不就重复了吗,所以遇到这样的情况,我们直接left++,直到left所指向的位置不等于0。

所以我们可能会这样写代码

while(nums[left] == nums[left+1]) left++

但我不推荐这样写,如果我们完成去重操作,对这个固定的数,left,right都这样写的话,有个测试用例会出错。

这里给大家画图讲解一下为什么会出错。

这里我们完成固定-4的操作后,我们接下来固定-1,按照我们刚刚写的逻辑的话,我们会直接跳过这个-1,固定下一个-1,这就漏了一个用例了,当我们固定第一个-1的时候,[-1,-1,1]是满足条件的,如果直接跳过了,这一组的测试用例就会不通过。

所以我们去重操作可以这样写。

 while(nums[left] == nums[left - 1])

这里我们防止left和right出界,我们可以在去重操作增加一个判断——left < right

代码如下:

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        sort(nums.begin(),nums.end());
        vector<vector<int>> vec;
        int size = nums.size();
        int n = 0;
        while(n < size)
        {
            if(nums[n] > 0) break;
            int left = n + 1;
            int right = size - 1;
            while(left < right)
            {
                int target = -nums[n];
                if(nums[left] + nums[right] < target) left++;
                else if(nums[left] + nums[right] > target) right--;
                else{
                    vec.push_back({nums[n],nums[left],nums[right]});
                    left++;
                    right--;
                     while(left < right &&nums[left] == nums[left - 1]) left++;
                     while(left < right &&nums[right] == nums[right + 1]) right--;
                }
            }
            n++;
            while(n < size  && nums[n] == nums[n - 1]) n++;
        }
        return vec;
    }
};

四、力扣18——四数之和

题目如下:

四数之和等于给出的一个target,就是让我们在这个数组上找四个数相加=target。

在我们讲解了两数之和,三数之和,这里大家应该都会有一个思路吧 

这里我们固定第一个数-2,这个问题就转化成在这个数组中找到三数之和找到target = 3的数,然后我们在除去固定数的数组中,再固定第一个数-1,这个问题就转化成在这个数组中找到两数之和找到target = 4,当然在我们所画的数组并不存在。因为这只是我们固定-2的结果,我们要一直往后固定,一直模拟这个算法。

这道题也是需要去重的,去重的思路和三数之和的思路一样,我也不继续细说啦

代码如下:

class Solution {
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) {
        sort(nums.begin(),nums.end());
        vector<vector<int>> vec;
        int size = nums.size();
        int n = 0;
        while(n < size)
        {
            int Bindn = n + 1;
            while(Bindn < size)
            {
                int left = Bindn + 1;
                int right = size - 1;
                while(left < right)
                {
                    long cnt = (long)target - nums[n] - nums[Bindn];
                    if(nums[left] + nums[right] < cnt) left++;
                    else if(nums[left] + nums[right] > cnt) right--;
                    else{
                            vec.push_back({nums[n],nums[left],nums[right],nums[Bindn]});
                            left++;
                            right--;
                            while(left < right &&nums[left] == nums[left - 1]) left++;
                            while(left < right &&nums[right] == nums[right + 1]) right--;
                 }
                }
                Bindn++;
                while(Bindn < size  && nums[Bindn] == nums[Bindn - 1]) Bindn++;
            }
            n++;
            while(n < size && nums[n] == nums[n - 1]) n++;
        }
        return vec;
    }
};


网站公告

今日签到

点亮在社区的每一天
去签到