【优选算法篇】:双指针算法--开启高效编码的两把“魔法指针”,练习题演练

✨感谢您阅读本篇文章,文章内容是个人学习笔记的整理,如果哪里有误的话还请您指正噢✨
✨ 个人主页:余辉zmh--CSDN博客
✨ 文章所属专栏:c++篇--CSDN博客

文章目录

一.双指针算法

双指针算法(Two Pointers Algorithm),又称快慢指针算法,龟兔赛跑算法等,是一种在链表,数组,矩阵等数据结构中,通过设置两个指针,并通过特定的移动策略来遍历数据元素同时解决问题的编程技巧。

双指针的分类:

  • 对撞指针:两个指针方向相反移动
  • 快慢指针:两个指针方向相同,但移动速度不同,通常快指针每次移动的距离大于慢指针
  • 分离双指针:两个指针分别属于不同的数组或者链表

双指针算法的优势:

双指针算法最核心的用途是优化时间复杂度。原本两个指针有n2中组合,时间复杂度是O(n2)。而双指针算法通过运用单调性,使得指针只能单项移动,因此总的时间复杂度只有O(n)。

下面通过几个例题来讲解双指针的使用技巧

二.例题

1.移动零

链接:leetcode.283.移动零

题目:

算法原理:

代码实现:

cpp 复制代码
void moveZeroes(vector<int>& nums) {
    //双指针算法
    int cur=0;
    int dest=-1;
    while(cur<nums.size()){
        //cur指针遇到0,cur向前移动
        if(nums[cur]==0){
            cur++;
        }
        //cur指针遇到非0,交换cur指针和dest+1指针指向的值,交换完后两个指针再向前移动
        else{
            swap(nums[cur],nums[dest+1]);
            dest++;
            cur++;
        }
    }
}

2.复写零

链接:leetcode.1089.复写零
题目:

算法原理:

代码实现:

cpp 复制代码
void duplicateZeros(vector<int> &arr)
{
    int cur = 0;
    int dest = -1;

    // 先找到复写后的最后一个元素位置
    while (dest < (int)arr.size() - 1)
    {
        if (arr[cur])
        {
            dest += 1;
        }
        else
        {
            dest += 2;
        }
        if (dest < arr.size() - 1)
        {
            cur++;
        }
    }

    // 处理边界情况
    if (dest == arr.size())
    {
        arr[arr.size() - 1] = 0;
        cur--;
        dest -= 2;
    }

    while (cur >= 0)
    {
        // cur指针遇到非0元素,复写一次,两个指针都往前移动一次
        if (arr[cur])
        {
            arr[dest--] = arr[cur--];
        }
        // cur指针遇到0元素,复写两次,dest指针先移动一次,两个指针再一起移动一次
        else
        {
            arr[dest--] = 0;
            arr[dest--] = 0;
            cur--;
        }
    }
}

3.快乐数

链接:leetcode.3.快乐数
题目:

算法原理:

代码实现:

cpp 复制代码
int number(int n)
{
    int sum = 0;
    while (n)
    {
        int m = n % 10;
        n /= 10;
        sum += m * m;
    }

    return sum;
}
bool isHappy(int n)
{
    int slow = n;
    int fast = number(n);
    while (slow != fast)
    {
        slow = number(slow);
        fast = number(number(fast));
    }

    return slow == 1;
}

4.盛水最多的容器

链接:leetcode.11.盛最多水的容器
题目:

算法原理:

代码实现:

cpp 复制代码
int maxArea(vector<int> &height)
{
    // 定义两个左右指针
    int left = 0;
    int right = height.size() - 1;

    // 获取小的高度
    int min = height[left] < height[right] ? height[left] : height[right];

    // 求出当前容量假设为最大值
    int vmax = min * (right - left);

    // 左右两个指针向内移动,容器的宽度减少,想要获取更大的容量,只能找到大的高度
    // 也就是舍弃小的高度,移动找大的高度
    while (left < right)
    {
        if (height[left] < height[right])
        {
            left++;
        }
        else
        {
            right--;
        }

        min = height[left] < height[right] ? height[left] : height[right];

        int v = min * (right - left);

        if (v > vmax)
        {
            vmax = v;
        }
    }

    return vmax;
}

5.有效三角形的个数

链接:leetcode.5.有效三角形的个数
题目:

算法原理:

代码实现:

cpp 复制代码
int triangleNumber(vector<int>& nums) {
    //先将数组进行排序,可以减少三角形的判断情况
    sort(nums.begin(),nums.end());

    //设置最大值的指针max,在max指针左区间设置两个做右指针有来查找符合的组合
    int max=nums.size()-1;
    int left=0;
    int right=max-1;

    //设置变量size用来记录可以组合的个数
    int size=0;

    while(max>0){
        //如果左指针值加上右指针值大于max指针最大值,[left,right-1]区间都符合,求出个数
        //右指针减一
        if(left<right&&nums[left]+nums[right]>nums[max]){
            size+=(right-left);
            right--;
        }
        //如果左指针值加上右指针值小于等于max指针最大值,left指针加一
        else{
            left++;
        }
        //当左右指针相遇时,从新设置最大值,在[left,max-1]区间继续查找
        if(left>=right){
            max--;
            left=0;
            right=max-1;
        }
     }

     return size;
}

6.和为s的两个数

题目:

算法原理:

代码实现:

cpp 复制代码
vector<int> twosum(vector<int>& nums,int target){
    int left = 0;
    int right = nums.size() - 1;
    vector<int> v;

    while(left<right){
        if(nums[left]+nums[right]<target){
            left++;
        }
        else if(nums[left]+nums[right]>target){
            right--;
        }
        else{
            v.push_back(nums[left]);
            v.push_back(nums[right]);
            break;
        }
    }

    return v;
}

7.三数之和

链接:leetcode.7.三数之和
题目:

算法原理:

因为题目要求返回的是数组元素,不是下标,所以可以先对数组进行排序,使其递增排列,这样就能使用两数之和的思想。除此之外还有一个注意点就是,每次找到符合要求的组合后,在移动指针时要判断是否会出现重复元素,如果出现重复元素要去重,减少不必要的遍历,可以降低时间复杂度。

代码实现:

cpp 复制代码
vector<vector<int>> threesum(vector<int>& nums){
    //先将数组进行排序
    sort(nums.begin(), nums.end());
    //定义一个二维数组用来存放组合数组
    vector<vector<int>> vv;

    //定义三个指针
    int i = 0;
    int left = i + 1;
    int right = nums.size() - 1;

    int target = 0 - nums[i];

    //i指针指向的是小于等于零的元素,这样可以保证三数之和为0
    while(nums[i]<=0&&left<right){
        if(nums[left]+nums[right]>target){
            right--;
        }
        else if(nums[left]+nums[right]<target){
            left++;
        }
        else{
            vv.push_back({nums[i],nums[left++],nums[right--]});
            
            //左右指针遇到相等元素时,跳过去重
            //处理边界情况,左指针要保证在右指针的左边,右指针要保证在左指针的右边
            while(left<right&&nums[left]==nums[left-1]){
                left++;
            }
            while(right>left&&nums[right]==nums[right+1]){
                right--;
            }
        }

        //当出现左右指针相等或者互换方向时,重新设置i指针的值,同时i指针也要去重
        //处理边界情况,i可能会越界
        if(left>=right){
            i++;
            while(i+1<nums.size()&&nums[i-1]==nums[i]){
                i++;
            }
            left=i+1;
            right = nums.size() - 1;
            target = 0 - nums[i];
        }
    }

    return vv;
}

8.四数之和

链接:leetcode.18.四数之和
题目:

算法原理:

四数之和这里就不再详细地讲解算法原理,其实就是三数之和的改版,在三数之和的基础上再增加一个指针

i指针固定第一个数,j指针固定第二个数,两数之和值=target-nums[i]=nums[j]

在三数之和的基础上增加一个循环,同时也要注意去重

如果前面两数之和,三数之和明白算法原理后,这里四数之和就会比较轻松。所以一定要先明白前面两个,再来尝试解这道题

代码实现:

cpp 复制代码
vector<vector<int>> foursum(vector<int>& nums,int target){
    //先排序
    sort(nums.begin(),nums.end());

    vector<vector<int>> vv;

    int left, right;

    //i指针固定第一个值
    for(int i=0;i<nums.size();){
        //j指针用来固定第二个值
        for (int j = i + 1; j < nums.size();){
            long long targetj = (long long)target - nums[i] - nums[j];
            left = j + 1;
            right = nums.size() - 1;

            while(left<right){
                if(nums[left]+nums[right]>targetj){
                    right--;
                }
                else if(nums[left]+nums[right]<targetj){
                    left++;
                }
                else{
                    vv.push_back({nums[i], nums[j], nums[left++], nums[right--]});

                    //去重,同时处理边界情况
                    while(left<right&&nums[left]==nums[left-1]){
                        left++;
                    }
                    while (left < right && nums[right] == nums[right + 1]) {
                        right--;
                    }
                }
            }

            j++;
            while(j<nums.size()&&nums[j]==nums[j-1]){
                j++;
            }
        }

        i++;
        while(i<nums.size()&&nums[i]==nums[i-1]){
            i++;
        }
    }

    return vv;
}

以上就是关于双指针算法的讲解,如果哪里有错的话,可以在评论区指正,也欢迎大家一起讨论学习,如果对你的学习有帮助的话,点点赞关注支持一下吧!!!

相关推荐
机器学习之心17 分钟前
一区正弦余弦算法!SCA-SVM正弦余弦算法优化支持向量机多特征分类预测
算法·支持向量机·分类·sca-svm·正弦余弦算法优化
a栋栋栋1 小时前
刷算法心得
算法
華華3551 小时前
读程序题...
开发语言·c++·算法
宸码2 小时前
【机器学习】手写数字识别的最优解:CNN+Softmax、Sigmoid与SVM的对比实战
人工智能·python·神经网络·算法·机器学习·支持向量机·cnn
太空漫步112 小时前
逆序对个数
java·算法·排序算法
23级二本计科2 小时前
分治_归并_归并排序(逆序对)
数据结构·算法·排序算法
CHENWENFEIc2 小时前
基础排序算法详解:冒泡排序、选择排序与插入排序
c语言·数据结构·算法·排序算法·学习方法·冒泡排序
养佳工具人2 小时前
Java各种排序
java·算法·排序算法
都适、隶仁ミ2 小时前
【密码学】SM4算法
linux·运维·服务器·算法·网络安全·密码学·网络攻击模型
C++oj3 小时前
普及组集训--图论最短路径设分层图
数据结构·算法·图论·最短路径算法