不到十个例题带你拿下c++双指针算法(leetcode)

移动零问题

https://leetcode.cn/problems/move-zeroes/submissions/

1.题目解析

必须在原数组进行修改,不可以新建一个数组

非零元素相对顺序不变

2.算法原理

【数组划分】【数组分块】

这一类题会给我们一个数组,让我们划分区间,比如说这题,最后会划分为两个区间,前一段是非零元素,后一段是零,一般我们只要看到这样的特性,脑海里就应该想到用双指针算法来解决(利用数组下标充当指针)

定义两个指针:

两个指针的作用:

cur :从左往后扫描数组,遍历数组。

dest : 已处理的区间内,非零元素的最后一个位置

如何做到:

cur从前往后遍历的过程中:

1.遇到0元素:cur++;

2.遇到非零元素:

交换

相关知识:快速排序

双指针的思想其实就是快排的核心思想

class Solution {
public:
    void moveZeroes(vector<int>& nums) {
       for(int cur=0,dest=-1;cur<nums.size();cur++)
       {
           if(nums[cur])
           {
               swap(nums[++dest],nums[cur]);
           }
       }
    }
};

 

复写零问题

https://leetcode.cn/problems/duplicate-zeros/

1.题目解析

长度固定:不能越界

在原始数组进行操作

2.算法原理:

解法:双指针算法 先根据异地操作,然后优化成双指针下的“就地操作”

根据异地操作,得出先模拟一遍复写的操作,然后从后向前进行覆盖,从前往后会发生覆盖多的值。

总结:

1.先找到最后一个复写的数

用双指针模拟一遍 cur=0,dest=-1;

先判断cur位置的值,决定dest向后移动几步,判断dest是否已经到了最后的位置,cur++;

2.从后向前进行操作

特例:数组越界

所以要加上一个步骤:处理越界

3.解题代码:

class Solution {
public:
    void duplicateZeros(vector<int>& arr) {
        int cur=0,dest=-1,n=arr.size();
        //先模拟
        while(cur<n)
        {
            if(arr[cur]) dest++;
            else         dest+=2;
            if(dest>=n-1) break;
            cur++;
        }
        //判断边界
        if(dest==n)
        {
            arr[n-1]=0;
            dest-=2;
            cur--;
        }
        //从后向前
        while(cur>=0)
        {
            if(arr[cur]==0)
            {
                arr[dest--]=0;
                arr[dest--]=0;
                cur--;
            }
            else arr[dest--]=arr[cur--];
        }
    }
};

 

快乐数问题

https://leetcode.cn/problems/happy-number/

1.题目解析

一直替换

分为两种情况:

变成一

无限循环

两种情况

2.算法原理

有没有发现,一直平方到最后,一定会成环

解法:快慢双指针

1.定义快慢双指针

2.慢指针每次向后移动一步,快指针每次走两步

3.判断相遇的时候的值即可

双指针只是一种思想,不需要一定定义双指针,在这个题中快指针就是平方两次,慢指针平方一次

为什么会一定会重复

证明方法:

鸽巢原理(抽屉原理):

n个巢穴 n+1个鸽子-->至少有一个巢穴里面的鸽子数量大于1

3.解题代码

class Solution {
public:
    int Sum(int n)
    {
        int sum=0;
        while(n)
        {
            int g=n%10;
            sum+=g*g;
            n/=10;
        }
        return sum;
    }
    bool isHappy(int n) {
        int slow=n,fast=Sum(n);
        while(slow!=fast)
        {
            slow=Sum(slow);
            fast=Sum(Sum(fast));
        }
        return slow==1;
    }
};

盛水最多的容器

https://leetcode.cn/problems/container-with-most-water/

1.题目解析

只能水平放置,也就是找最大的矩形

2.算法原理

解法一:暴力枚举:双重循环

优点:想法简单

缺点:时间复杂度过高

解法二:头尾双指针:

v=h*w

宽度一直在减小,而高度有两种情况,变大或变小,在宽度一直在变小的情况下,我们高度必须选择更高的

3.解题代码


class Solution {
public:
    int maxArea(vector<int>& height) {
        int left=0,right=height.size()-1;
        int v=-1;
        while(left<right)
        {
            int tv=min(height[left],height[right])*(right-left);
            v=max(tv,v);
            if(height[left]>height[right])  right--;
            else                            left++;
        }
        return v;
    }
};

和为s的两个数字问题

1.题目解析

有序数组

随意输出一对

2.算法原理

解法一:暴力枚举 双重循环 n^2复杂度

解法二:利用单调性,使用双指针算法解决问题

首尾相加

判断数值,如果大于目标值,right--反之left++,因为数组是有序的

3.解题代码

vector<int> twosum(vector &nums,int t)
{
  int left =0,right=nums.size()-1;
  while(left<right)
  {
    int sum=nums[left]+nums[right];
    if(sum>t) right--;
    else if(sum<t) left++;
    else return {num[left],nums[right]};
  }
  //照顾编译器
  return {-1,-1};
}

有效三角形个数

https://leetcode.cn/problems/valid-triangle-number/

1.题目解析

相同的不算一个

2.讲解算法原理

补充数学知识:给我们三个数,判断是否能构成三角形

我们仅需要一个公式,就能判断是否能构成三角形:

如果我们已经知道三个数的大小顺序,只需要a+b>c(c是最大的数)

优化:先对整个数组排序

解法一:暴力枚举

for(i=0;i<n;i++)

for(j=i+1;j<n;j++)

    for(k=j+1;k<n;k++)

        check(i,j,k)

解法二:利用单调性,使用双指针算法来解决问题

①先固定最大的数

②在最大的数的左区间里,使用双指针算法,快速统计

3.解题代码

class Solution {
public:
    int triangleNumber(vector<int>& nums) 
    {
        int n=nums.size();
        int ret=0;
        sort(nums.begin(),nums.end());
        for(int i=n-1;i>=2;i--)
        {
            int left=0,right=i-1;
            while(left<right)
            {
                if(nums[left]+nums[right]>nums[i])
                {
                 ret+=right-left;
                 right--;
                }
                else
                {
                left++;
                }
            }
        }
        return ret;
    }
};

三数之和问题

https://leetcode.cn/problems/3sum/

1.题目解析

三个数的下标不同

答案不可重复(去重)

有可能没有答案

2.算法原理

解法一:先排序+暴力枚举+利用set去重o(n^3)

解法二:排序+双指针:

找到右边区间两数之和为左边数字的相反即可

1.排序

2.固定一个数a

3.在该数后边的区间,利用双指针的算法,快速找到和为-a的两个数

处理细节问题:

1.去重:

找到一种结果之后,left和right指针要跳过重复元素。

当使用完一次双指针算法之后,i也要跳过重复元素(避免越界)

2.不漏:

找到一种结果后,不要“停”,缩小区间,继续寻找

3。解题代码

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        sort(nums.begin(),nums.end());
        vector<vector<int>> ret;
        int n=nums.size();
        for(int i=0;i<n;)
        {
            if(nums[i]>0) break;
            int left=i+1,right=n-1,t=-nums[i];
            while(left<right)
            {
                int sum=nums[left]+nums[right];
                if(sum<t) left++;
                else if(sum>t) right--;
                else
                {
                    ret.push_back({nums[i],nums[left],nums[right]});
                    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 ret;
    }
};

四数之和

https://leetcode.cn/problems/4sum/

1.题目解析

三数之和的进阶版本,基本算法原理和三数之和大差不差

2.算法原理

解法一:排序+暴力枚举

四个循环,绝对超时

解法二:排序+双指针

1.依次固定一个数i

2.在i后面的区间内,利用三数之和找到三个数,让他们三个的和等于target-i;

{

1.依次固定一个j

2.在j后边的区间里,利用“双指针找到两个数,使得这两个数之和等于target-i-j”

}

处理细节问题:

不重,不漏

3.解题代码:

class Solution {
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) {
              vector<vector<int>> ret;
            //1.排序
            sort(nums.begin(),nums.end());
            //2.利用双指针解决问题
            int n=nums.size();
            for(int i=0;i<n;)
            {
                //利用三数之和
                for(int j=i+1;j<n;)
                {
                    int left=j+1,right=n-1;
                    long long aim=(long long)target-nums[i]-nums[j];
                    while(left<right)
                    {
                        int sum=nums[left]+nums[right];
                        if(sum<aim) left++;
                        else if(sum>aim) right--;
                        else
                        {
                            ret.push_back({nums[i],nums[j],nums[left++],nums[right--]});
                        //去重1
                        while(left<right&&nums[left]==nums[left-1]) left++;
                        while(left<right&&nums[right]==nums[right+1]) right--;
                        }
                    }
                        //去重2
                j++;
                while(j<n&&nums[j]==nums[j-1]) j++;			
                }
                //去重3
                i++;
                while(i<n&&nums[i]==nums[i-1]) i++;
            }  
            return ret;
    }
};

觉得有帮助的惯用老爷麻烦点点关注!!!

  • 7
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

贰月磐石

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

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

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

打赏作者

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

抵扣说明:

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

余额充值