浅谈双指针算法

news2024/12/26 23:03:51

目录

算法概述

案例分析

1、删除有序数组中的重复项

2、环形链表

3、盛最多水的容器

4、有效三角形的个数

5、三数之和

6、1089. 复写零

内容总结


算法概述

双指针指的是在遍历元素的过程中,不是使用单个指针进行访问,而是使用两个指针进行访问,从而达到相应的目的。一些特殊的情况利用双指针可以将原本时间复杂度为O(N²)的操作简化为O(N)级别的。而且,人们根据大量的做题经验归纳出了几种特殊的双指针算法,例如「对撞指针」、「快慢指针」、「分离双指针」等。其中,对撞指针就是指的两个指针从范围的两端相向而行,快慢指针就是指两个指针的移动速度或是每次移动的范围有快慢之分,分离双指针就是指的两个指针分别在两个不同的数组或是容器中。

当然,干巴巴的说概念没多大意义,接下来就让我们通过具体的例题来感受这个双指针算法。

案例分析

1、删除有序数组中的重复项

题目描述

26. 删除有序数组中的重复项icon-default.png?t=N7T8https://leetcode.cn/problems/remove-duplicates-from-sorted-array/思路分析

这题是一个很常规的双指针题目,用到的就是快慢指针,下面是具体的思路分析:

首先注意数组是有序的,那么重复的元素一定会相邻。要求删除重复元素,实际上就是将不重复的元素移到数组的左侧。所以我们考虑用 2 个指针,用来维护两个范围,一个是当前的有效范围,即没有重复元素的范围,另一个是当前已经处理过的范围。假设我们用eff维护有效范围,cur用于维护当前范围,即 [0, eff] 区间内的是有效范围,cur指针用于枚举数组。

那么我们就可以判断cur位置的数是否等于cur-1,如果等于,就说明是重复元素,直接忽略(不能加入有效范围),如果不等于,那么就将 cur 位置的数和 eff+1 位置的数交换,然后令eff自增1。这样当cur走到最后时,eff所在的位置,就是有效数组的区间范围。

如下是我的题解,仅供参考:

class Solution {
public:
    int removeDuplicates(vector<int>& nums) 
    {
        int eff = 1, cur = 0;
        while(++cur < nums.size())
        {
            if(nums[cur] != nums[cur - 1])
            {
                nums[eff++] = nums[cur];
            }
        }
        return eff;
    }
};

2、环形链表

题目描述

141. 环形链表icon-default.png?t=N7T8https://leetcode.cn/problems/linked-list-cycle/思路分析

这是一个很经典的链表题目,如下是具体的思路分析:

这题就是属于那种知道就会,不知道就不会的那种。因为大多数人第一次接触这题很难有思路,但一旦知道怎么做之后很快就记住了。

好了我们不多废话,直接看解法:先定义一对快慢指针fast和slow,它们分别都从头开始,fast每次向后走两步,slow每次向后走一步。如果出现fast==slow的情况,那么就是有环。否则如果fast如果走到了NULL,那就说明没环。

至于证明,感兴趣的可以到下面这篇博客中找,有对应一样的题目,附带证明。

链表题目强化练_小白菜※的博客-CSDN博客

如下是我的题解,仅供参考:

bool hasCycle(struct ListNode *head) 
{
    if(head == NULL)
        return false;
    struct ListNode *fast = head, *slow = head;
    while(fast && fast->next)
    {
        slow = slow->next;
        fast = fast->next->next;
        if(slow == fast)
            return true;
    }
    return false;
}

3、盛最多水的容器

题目描述

11. 盛最多水的容器icon-default.png?t=N7T8https://leetcode.cn/problems/container-with-most-water/思路分析

这是一道很经典的面试题,但大多数人看到这题时很难想到用双指针的解法:

在解决这道题之前,我们先要归纳出容纳的水的面积公式为:width*high。而这个width就是指的两个“柱子”之间的距离,而high就是指的两个“柱子”之中的较短者。

所以,我们可以控制让其中一个因素具有单调性,这样另一个因素就可以根据这个单调性来“对症下药”了。具体的做法是:让左右两个指针先在数组的左右两端,这样就保证了width的最大,这样不管下一个指针怎么移动,width都是在单调递减的,所以要想保证获取的下一个面积有相对较大的可能,我们就要舍弃掉左右指针中较小的那一个。这期间还需要有一个变量用于保存结果并不断与当前面积比较大小并获取最大。

如下是我的题解,仅供参考:

class Solution {
public:
    int maxArea(vector<int>& height) 
    {
        int left = 0;
        int right = height.size() - 1;
        int max_area = 0;
        while(left < right)
        {
            // 走一趟,求出最大
            int high = std::min(height[left], height[right]);
            int cur_area = (right - left) * high;
            max_area = std::max(max_area, cur_area);
            // 谁小让谁走,一样小的,无所谓
            if(high == height[left])
                left++;
            else 
                right--;
        }
        return max_area;
    }
};

4、有效三角形的个数

题目描述

611. 有效三角形的个数icon-default.png?t=N7T8https://leetcode.cn/problems/valid-triangle-number/思路分析

首先我们需要知道形成三角形的条件:当两条较小边之和大于另一条边时,就一定可以形成三角形。所以我们可以先对数组排序,然后想办法利用这个有序的条件进行操作。

而数组有序之后,由于有三条边需要控制,我们很难一下子控制三个变量,所以我们可以先固定一个变量,比如要当作最大或最小的那条边,我们这里以最大的为例,那么固定值就从后往前(从大到小)进行枚举。我们就对固定值左边的范围去寻找对应的符合范围的两条边,那么这样就好处理了:采用对撞数组的方式,让两个指针相向而行,如果和大于固定值,就说明可以组成三角形,那么就将个数加上左右指针之间的差值(因为此时这个范围内的数与右指针相加是一定大于固定值的),随后让右指针左移一个并继续。如果和小于固定值,就需要让左指针右移一个,然后继续判断了。

如下是我的题解,仅供参考:

class Solution 
{
public:
    // 思路:先排序,然后根据有序数组利用双指针来做
    int triangleNumber(vector<int>& nums) 
    {
        // 排序,时间复杂度 O(logN)
        std::sort(nums.begin(), nums.end(), std::less()); 
        // 双指针:先固定一个,然后i、j指针在剩余区域相向而行
        int count = 0;
        for(int k = nums.size() - 1; k > 1; k--)
        {
            int i = 0, j = k - 1;
            while(i < j)
            {
                if(nums[i] + nums[j] > nums[k])
                {
                    count += j - i;
                    j--;
                }
                else
                {
                    i++;
                }
            }
        }

        return count;
    }
};

5、三数之和

题目描述

15. 三数之和icon-default.png?t=N7T8https://leetcode.cn/problems/3sum/思路分析

这题其实如果没有要求不包含重复项这一条件还并不是很难,但就是因为这个要求使得很多人(包括我)卡了很久。

如果不考虑“不包含重复项”这一条件,那么大致的思路就可以是:先对数组排序,然后从后往前枚举k,接着对k左边的区域采取对撞指针的方式,就可以找出所有符合条件的三元组。这时我们再来额外的考虑这个“不包含重复项”的条件。由于数组是已经排序好的,所以相同的数据一定是相邻的,那么此时我们只需要对i、j、k每一个单独使其不处理相同数据的情况不就行了吗?这里的思路就和第一个案例很像了,遇到 nums[cur] == nums[cur-1] 的情况就直接跳过不处理即可。

如下是我的题解,仅供参考:

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) 
    {
        // nums[i] + nums[j] + nums[k] = 0 
        // 先排序,然后固定一个(k),通过双指针来确定剩下的两个{i、j}
        // 由于数组是有序的,所以去重操作只需要忽略当前项等于前一个的情况即可
        sort(nums.begin(), nums.end());       
        vector<vector<int>> ans;
        int k = nums.size() - 1;
        while(k > 1)
        {
            int i = 0, j = k - 1;
            while(i < j)
            { 
                if(nums[i] + nums[j] + nums[k] > 0) j--;
                else  if(nums[i] + nums[j] + nums[k] < 0) i++;
                else
                {
                    //添加完之后i和j继续移动,防止死循环
                    ans.push_back({nums[i], nums[j], nums[k]});            
                    int ipast = nums[i];
                    int jpast = nums[j];
                    while(i < j && nums[i] == ipast) i++;   
                    while(i < j && nums[j] == jpast) j--;                    
                }
            }            
            int past = nums[k];
            while(k > 1 && nums[k] == past) k--;     
        } 
        
        return ans;
    } 
}; 

6、1089. 复写零

题目描述

1089. 复写零icon-default.png?t=N7T8https://leetcode.cn/problems/duplicate-zeros/思路分析

这题其实很容易想到的就是借助一个额外的数组,然后在另一个数组中进行操作,这样就可以很容易的得出答案。但是,由于题目要求我们必须就地操作,所以我们不能用这种方法。不过我们可以根据这种方法,来推导模拟出就地操作的方法。

大致思路就是:设置两个指针,先让它们模拟一次在两个数组中操作的情况下,结束时这两个指针的位置,然后根据位置从后往前推就可以了。只不过有一种特殊情况,就是模拟异地的那个指针可能会出现越界的情况,这是因为有一种最后多出一个0的情况,但由于这种情况非常的特殊,所以我们只需要对这一种情况特殊处理就可以了。

如下是我的题解,仅供参考:

class Solution {
public:
    void duplicateZeros(vector<int>& arr) 
    {
        // 思路:面向结果编程。通过异地数组的方式推导就地数组的规律
        int cur = 0, eff = 0;
        while(eff < arr.size())
        {
            if(arr[cur] == 0)
            {
                eff++;
            }
            cur++;
            eff++;                
        }

        if(--cur != --eff)
        {
            // 特殊情况/细节处理:模拟的出现越界的情况,那么一定是0的问题
            if(eff == arr.size())
            {
                arr[--eff] = arr[cur];
                eff--;
                cur--;
            }

            while(cur >= 0)
            {
                if(arr[cur] == 0)
                {
                    arr[eff--] = 0;
                }
                
                arr[eff--] = arr[cur--];
            }
        }

    }
};

内容总结

在数组有序的情况下就要首先考虑二分和双指针,而对于数组有一定单调性或者某一因素具有一定单调性的时候,也是可以考虑使用双指针的。上面的几个案例只是为了帮助我们更好的理解、感受这种思路,并没有囊括所有的双指针题型。

其实双指针并不是一种算法,更倾向于是一种思路。有时双指针并不一定是解题的关键,有些时候只是解题的某一小部分,比如快速排序。所以我们不要拘泥于双指针算法或者双指针题型,这只是一种思想,或者处理数据的一种方式,并不是一种模板或者套路。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1033246.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Linux启动过程详解 Xmind导图笔记

参考大佬博客&#xff1a; 简要描述linux系统从开机到登陆界面的启动过程 Linux启动过程详解 Bootloader详解 来源&#xff1a;从BIOS开始画图了解Linux启动过程——老杨Linux

WBS字典解释和举例

定义 WBS词典通常包括&#xff1a;编码、工作包描述&#xff08;内容&#xff09;、成本预算、时间安排、质量标准或要求、责任人或部门或外部单位&#xff08;委托项目&#xff09;、资源配置情况、其他属性等。 实例

Qt5开发及实例V2.0-第十五章-Qt单元测试框架

Qt5开发及实例V2.0-第十五章-Qt单元测试框架 第15章 Qt 5单元测试框架15.1 QTestLib框架15.2 简单的Qt单元测试15.3 数据驱动测试15.4 简单性能测试 本章相关例程源码下载1.Qt5开发及实例_CH1501.rar 下载2.Qt5开发及实例_CH1502.rar 下载3.Qt5开发及实例_CH1503.rar 下载4.Qt5…

【李宏毅 | 深度学习】自注意力机制(Self-attention)

这里写目录标题 引言Sequence LabelingSelf-attention矩阵乘法Muti-head Self-attention&#xff08;多头注意力机制&#xff09; 引言 以往我们遇到的深度学习问题中&#xff0c;对于神经网络的输入一般都是一个向量&#xff0c;输出可能是一个类别。如果增加输入的复杂度&am…

基于open CV实现YOLOv3复现_预测阶段和后处理阶段

基于open CV实现YOLOv3复现_预测阶段和后处理阶段 1.导入所需的库&#xff1a;2.对输入的图像进行resize3.将图像输入yolov3的网络中进行预测&#xff0c;对三个特征层进行解码。4.非极大值抑制来去除多余的预测框完整代码 当训练好了模型后&#xff0c;用训练好的权重文件进行…

tftpd文件传输工具的学习记录

1.目的&#xff1a;在SOC板上的linux系统和本地电脑的windows系统进行文件的传输。 2.在windows中安装tftp服务器,其下载的文件如下&#xff1a; 链接: https://pan.baidu.com/s/1YN5WxcjqCJLHTtjhUtKbjg 提取码: 3cg9 3.打开软件&#xff0c;在当前目录下选择windows传输的…

TCP协议中常见的问题

文章目录 TCP协议中常见的问题谈一谈对OSI七层模型和TCP/IP四层模型的理解&#xff1f;谈谈TCP协议的3次握手过程&#xff1f;TCP协议为什么要3次握手&#xff1f;2次&#xff0c;4次不行吗&#xff1f;谈谈TCP协议的四次挥手过程&#xff1f;什么是流量控制&#xff1f;什么是…

报错处理:Error: Redis server is running but Redis CLI cannot connect

嗨&#xff0c;读者朋友们&#xff01;今天我来跟大家分享一个我在运维过程中遇到的一个关于Linux上运行Redis服务时的报错及解决方法。 报错信息如下&#xff1a; Error: Redis server is running but Redis CLI cannot connect 这个报错信息表明Redis服务器已经运行&#xff…

stm32之GPIO库函数点灯分析

stm32官方为了方便开发者&#xff0c;利用CubeMX 生成HAL库有关的C代码。HAL库就是硬件抽象层(hardware abstraction layer)&#xff0c;生成一系列的函数帮助我们快速生成工程&#xff0c;脱离复杂的寄存器配置。stm32相对于51来功能强大&#xff0c;但是寄存器的数量也不是一…

动手吧,vue数字动画

数字动画&#xff0c;有数字的地方都能用上&#xff0c;拿去吧&#xff01; 效果&#xff1a; 1、template部分 <template><div class"v-count-up">{{ dispVlaue }}</div> </template> 2、js部分 export default {data() {return {timer…

版本动态 | SolidUI 0.3.0 版本发布

文章目录 背景发版清单功能部署 示例html生成模型选择数据源 详细指引贡献者如何成为贡献者 背景 随着文本生成图像的语言模型兴起&#xff0c;SolidUI想帮人们快速构建可视化工具&#xff0c;可视化内容包括2D,3D,3D场景&#xff0c;从而快速构三维数据演示场景。SolidUI 是一…

摄像头工程师说 Camera - 数据格式 RAW、RGB(1)

Camera - 数据格式 RAW、RGB Camera 数据格式-RAW、RGB从摄像头工作的基本原理说起回归本质-图像色彩的几种表示方法RGB 三原色RAW RGB 格式诞生RAW8 VS RAW10真彩色-RGB888、BGR888 格式的引入数据量小点的 RGB 格式-RGB565、RGB555用位数表示的 RGB 格式RGB24&#xff1a;RGB…

iOS17.0.2更新修复iPhone 15系列机型数据迁移问题,附新机快速数据迁移办法!

iPhone 15 系列机型已于今日正式发售&#xff0c;为解决iPhone15这些机型出现的数据迁移问题&#xff0c;苹果紧急发布了 iOS 17.0.2 更新&#xff0c;内部版本号为 21A350。 需要注意的是&#xff0c; iOS 17.0.2 更新仅适用于 iPhone 15、iPhone 15 Plus、iPhone 15 Pro 和 …

HTML5福利篇--使用Canvas画图

目录 一.Canvas元素 1.Canvas元素定义 2.使用JavaScript获取页面中的Canvas对象 二.绘制图形 1.绘制直线 2.绘制矩形 &#xff08;1&#xff09;rect() &#xff08;2&#xff09;strokeRect() &#xff08;3&#xff09;fillRect()和clearRect()函数 3.绘制圆弧 4.…

华为云云耀云服务器L实例评测 | 基于minikube搭建单节点kubernetes集群

目录 1 安装Docker2 conntrack-tools3 安装minikube4 下载二进制&#xff1a;kubeadm、kubectl、kubelet5 准备镜像6 启动minikube7 简单测试 ​ Minikube 是一个使用golang开发的单节点kubernetes集群环境&#xff0c;在资源紧张的情况下&#xff0c;可以用于快速搭建kubernet…

项目进展(一)-晶振正常输出、焊接驱动芯片、查找芯片手册并学习

今天的主要工作集中在博士师兄的项目上&#xff0c;效率偏低&#xff0c;主要是一中午的时间都卡在晶振上。在焊接完芯片和晶振之后&#xff0c;测试晶振输出引脚无输出&#xff0c;所以就开始找各种博客&#xff0c;寻找晶振不起振的原因&#xff0c;在下面两篇文章中找到了答…

基于骨架的动作识别:SkeleTR: Towrads Skeleton-based Action Recognition in the Wild

论文作者&#xff1a;Haodong Duan,Mingze Xu,Bing Shuai,Davide Modolo,Zhuowen Tu,Joseph Tighe,Alessandro Bergamo 作者单位&#xff1a;The Chinese University of Hong Kong; AWS AI Labs. 论文链接&#xff1a;http://arxiv.org/abs/2309.11445v1 内容简介&#xff1…

JavaScript+canvas实现粒子动画效果

1.HTML部分 <!DOCTYPE html> <html lang"zh"><head><meta charset"UTF-8" /><meta name"viewport" content"widthdevice-width, initial-scale1.0" /><title>粒子效果</title><style&g…

OpenAI官方吴达恩《ChatGPT Prompt Engineering 提示词工程师》(7)聊天机器人 / ChatBot

聊天机器人 / ChatBot 使用大型语言模型来构建你的自定义聊天机器人 在本视频中&#xff0c;你将学习使用OpenAI ChatCompletions格式的组件构建一个机器人。 环境准备 首先&#xff0c;我们将像往常一样设置OpenAI Python包。 import os import openai from dotenv import…

图像练习-答题卡opencv(02)

原图 结果 代码 // Load source imagecv::Mat src cv::imread("answer_card.jpg", cv::IMREAD_COLOR);if (src.empty()){return;}cv::Mat gray;cv::cvtColor(src, gray, cv::COLOR_BGR2GRAY);cv::Mat binary;double value cv::threshold(gray, binary, 0, 255, …