双指针解题

news2025/4/8 22:35:12

双指针的使用范围

对于暴力解法的时间复杂度来说,双指针一般可以将暴力解法的时间复杂度降低一个量级.

常⻅的双指针有两种形式,⼀种是对撞指针,⼀种是左右指针.

快慢指针

⼜称为⻳兔赛跑算法,其基本思想就是使⽤两个移动速度不同的指针在数组或链表等序列结构上移动。
这种⽅法对于处理环形链表或数组⾮常有⽤。
其实不单单是环形链表或者是数组,如果我们要研究的问题出现循环往复的情况时,均可考虑使⽤快慢指针的思想。
快慢指针的实现⽅式有很多种,最常⽤的⼀种就是:
• 在⼀次循环中,每次让慢的指针向后移动⼀位,⽽快的指针往后移动两位,实现⼀快⼀慢。

对撞指针

⼀般⽤于顺序结构中,也称左右指针。
• 对撞指针从两端向中间移动。⼀个指针从最左端开始,另⼀个从最右端开始,然后逐渐往中间逼
近。
• 对撞指针的终⽌条件⼀般是两个指针相遇或者错开(也可能在循环内部找到结果直接跳出循
环),也就是:
• left == right (两个指针指向同⼀个位置)
• left > right (两个指针错开)

练习题目

快慢指针

移动零

283. 移动零 - 力扣(LeetCode)

思路

cur从0开始,dest从-1开始,cur往后走,如果nums[cur]不为0, 则将nums[cur]和nums[++dest]交换

 ---->交换

后面的非0的值都会与前面存在的0进行交换,最终达到将0放到后面的效果.

代码
void moveZeroes(vector<int>& nums) 
    {
        for(int cur = 0, dest = -1; cur < nums.size(); cur++)
            if(nums[cur])
                swap(nums[cur], nums[++dest]);//前加加是用原值加一后的值进行操作
    }

注意dest是前++,这样才能对dest+1后的值进行操作!

复写零

1089. 复写零 - 力扣(LeetCode)

思路

这题双指针的做法是cur与dest从右向左,因为从左向右会导致后面需要后移的值被覆盖.由题意得4为最后一个复写的数是4,故可得(后续会提出计算cur的方法)

如果cur对应值不为零,则cur和dest同时向左移动,并且将cur对应值赋给dest,如图:

如果cur对应值为0,则dest对应值赋为0,且往左移动,再将移动后的对应值也赋为零,然后再往左移动.

------>

寻找cur开始所处的位置(最后复写的数):

cur和dest同时向右移动,如果cur对应值为0,则dest移动两步,直到dest到数组末尾为止.

防止dest越界

dest一次走两步可能走到nums[n],我们要防止这种情况发生:将最后一位置零cur左走一步,越界的dest往左走两步.        

代码
void duplicateZeros(vector<int>& arr) 
    {
        int cur = 0, dest = -1, n = arr.size();
        //找到cur起点
        while(cur < n)
        {
            if(arr[cur]) dest++;
            else dest += 2;
            if(dest >= n - 1) break;
            cur++;
        }  
        //dest出界的特殊判断
        if(dest == n)
        {
            arr[n - 1] = 0;
            cur--, dest -= 2;
        }
        //倒着走双指针
        while(cur >= 0)
        {
            if(arr[cur]) arr[dest--] = arr[cur--];
            else 
            {
                arr[dest--] = 0;
                arr[dest--] = 0;
                cur--;
            }
        }
    }

快乐数

202. 快乐数 - 力扣(LeetCode)

思路

这个题的数组下标不是简单的加加减减,而是需要进行位数平方和相加,所以我们需要对此操作封装一个函数

int intSum(int n)
{
    int sum = 0;
    while(n)
    {
        int t = n % 10;
        sum += t * t;
        n /= 10;
    }
    return sum;
}

是快乐数的例子:19

不是快乐数的例子:2

我们取快慢指针slow fast.

若该数是快乐数,则fast先变为1后不再改变,slow一定能相遇;

若该数不是快乐数,则进入循环,则fast相对slow速度为1,故也一定会相遇。

代码
int intSum(int n)
{
    int sum = 0;
    while(n)
    {
        int t = n % 10;
        sum += t * t;
        n /= 10;
    }
    return sum;
}

bool isHappy(int n) 
{
    int slow = n, fast = intSum(n);
    while(slow != fast)
    {
        slow = intSum(slow);
        fast = intSum(intSum(fast));
    }
    return slow == 1 ? true : false;
}

以上就是快慢指针的练习题,不难发现这些题中移动数组元素的情况比较多,或者是数组的循环追及情况比较多。

对撞指针

盛水最多的容器

11. 盛最多水的容器 - 力扣(LeetCode)

思路

我们不妨直接取左右指针left right,当左边高度小于右边时,因为最左边和最右边的底边长是最长的,其容积记为V.当right往左边走,无论其高是多少,容积一定比V小。所以我们应该让left往右走,这样容积可能增大;相反,当右边高度小于左边时,应该让right往左边走

如图:left对应值(高度)小于右边,应该left++

---->

计算每次移动后围成的容积,取其中的最大值即可.

代码
int maxArea(vector<int>& height) 
{
    int n = height.size();
    int left = 0, vol = 0, maxi = 0, right = n - 1;
    while(left != right)
    {
        
        vol = min(height[left], height[right]) * (right - left);
        maxi = max(maxi, vol);//取容积最大值
        //if(vol > maxi) maxi = vol;
        if(height[left] < height[right]) left++;
        else right--;
    }
    return maxi;
}

有效三角形的个数

611. 有效三角形的个数 - 力扣(LeetCode)

思路

固定最右边的数

        对于这类比大小的题,我们需要先对其进行排序,并且这样也可以避免每条边都比较一次;再通过左右指针相加和与固定的值(fixed)相比较,若left+right大于fixed,则left继续右移也和一定大于fixed,所以应该进行righ--,并且left右移的几个值都可以组成三角形,所以ret += right - left。

代码
int triangleNumber(vector<int>& nums) 
{
    int n = nums.size(), 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;
    
}

两数之和

LCR 179. 查找总价格为目标值的两个商品 - 力扣(LeetCode)

思路

    左值加右值大于target,则left再加只会更大,所以应该right左移;

    左值加右值小于target,则right再减只会更小,所以应该left右移;

代码
vector<int> twoSum(vector<int>& price, int target) 
{
    int n = price.size();
    int left = 0, right = n - 1;
    while(left < right)
    {
        int sum = price[left] + price[right];
        if(sum < target) left++;
        else if(sum > target) right--;
        else
        {
            //c++的隐式类型转换
            return {price[left], price[right]};
        }
    }
    return {-1, -1};
}

leetcode在else那里若没有返回完会报错,所以我们可以在最后加一个return {-1, -1};

三数之和

15. 三数之和 - 力扣(LeetCode)

思路

固定最左边的数

此题与判断三条边那题类似,固定一个值i,再判断剩下两数,总体思路与两数之和类似

注意

值得注意的是本题需要去重,我们分为两重去重:第一重是left与right的去重;第二重是i的去重

并且要防止越界问题!

此时便是fixed代表值-1连续出现了两个,需要去重;

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

left代表值1连续出现两个,需要去重;

right代表值4连续出现两个,需要去重。

while (nums[left - 1] == nums[left] && left < right) left++;
while (nums[right + 1] == nums[right] && left < right) right--;
代码
vector<vector<int>> threeSum(vector<int>& nums) 
{
    vector<vector<int>> ret;
    sort(nums.begin(), nums.end());
    int n = nums.size();
    for (int i = 0; i < n; )
    {
        if (nums[i] > 0) break;
        int left = i + 1, right = n - 1, res = -nums[i];

        while (left < right)
        {
            int sum = nums[left] + nums[right];
            if (sum > res) right--;
            else if (sum < res) left++;
            else
            {
                ret.push_back({ nums[i], nums[left], nums[right] });
                left++, right--;
                //left right去重
                while (nums[left - 1] == nums[left] && left < right) left++;
                while (nums[right + 1] == nums[right] && left < right) right--;
            }

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

如果三数之和看懂了,还可以尝试一下四数之和!

18. 四数之和 - 力扣(LeetCode)

综上,对撞指针多为判定值的和或判定值之间的关系。

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

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

相关文章

Linux安装Python2.7.5(centos自带同款)

卸载已安装的python,防止版本兼容问题 rpm -qa|grep python|xargs rpm -ev --allmatches --nodeps 删除残余文件 whereis python |xargs rm -frv 安装前提是已安装gcc和g gcc --version g --version 下载安装python2.7.5 https://www.python.org/downloads/release/pyt…

Linux多进程开发-常用命令

进程 进程是计算机中正在运行的程序的实例。每个进程都有自己的地址空间、内存、文件和设备、线程以及其他系统资源。操作系统通过调度和管理进程来实现多任务处理&#xff0c;使得多个进程可以同时运行并与用户交互。在操作系统中&#xff0c;进程是基本的资源分配单位&#x…

appium学习之二:adb命令

1、查看设备 adb devices 2、连接 adb connect IP:端口 3、安装 adb install xxx.apk 4、卸载 adb uninstall 【包名】 5、把对应目录下的1.txt文件传到手机sdcard下 adb push 1.txt /sdcard 6、进入对应的设备里 adb shell 7、切入sdcard目录 cd /sdcard 8、ls 查…

Redis篇-3--原理篇2--Redis事务

1、概述 Redis 提供了简单的事务机制&#xff0c;Redis 事务的本质是一组命令的集合。当执行redis事务时&#xff0c;即一次性按照添加顺序依次执行这些命令&#xff0c;中间不会被打断或者干扰。 Redis 的事务机制并不像关系型数据库中的事务那样提供完整的ACID特性&#xf…

简单网页制作提升用户体验和客户转化

在当今竞争激烈的市场中&#xff0c;用户体验和客户转化率往往是决定企业成败的关键。简单而高效的网页制作&#xff0c;正是提升用户体验和客户转化的重要手段之一。 首先&#xff0c;简洁的网页设计能够有效减轻用户的认知负担。当用户打开一个层次分明、界面整洁的网站时&am…

CDGA|数据治理:数据仓库”建设投入大、周期长怎么办?

在数据治理的广阔领域中&#xff0c;数据仓库的建设无疑是一项至关重要的任务。然而&#xff0c;这项任务往往伴随着巨大的投入和漫长的周期&#xff0c;成为许多企业面临的棘手问题。数据仓库作为数据存储、处理和分析的核心平台&#xff0c;其建设不仅需要大量的资金和技术支…

大中型水闸安全监测系统

一、背景 水闸作为水利工程中的重要组成部分&#xff0c;承担着调节水位、控制水流、防洪排涝等多重功能。然而&#xff0c;水闸在运行过程中会受到各种自然和人为因素的影响&#xff0c;导致其安全性能下降&#xff0c;甚至发生安全事故。为了保障水闸的安全运行&#xff0c;…

STL之空间配置器allocator

STL之空间配置器allocator 空间配置器的标准接口设计一个简单的空间配置器, JJ::allocator 具备次配置力(sub-allocation)的SGI空间配置器SGI标准的空间配置器, std::allocatorSGI特殊的空间配置器, std::alloc构造和析构基本工具:construct()和destroy()空间的配置与释放,std:…

人大金仓(KingBaseEs)数据库操作手册

人大金仓数据库&#xff08;KingbaseES&#xff09;是由北京人大金仓信息技术股份有限公司&#xff08;简称人大金仓&#xff09;自主研发的、具有自主知识产权的通用关系型数据库管理系统。 官方下载地址&#xff1a;KingbaseES 人大金仓数据库 KES技术文档在线手册&#xf…

容器镜像仓库

文章目录 1、docker hub1_注册2_登录3_创建容器镜像仓库4_在本地登录Docker Hub5_上传容器镜像6_下载容器镜像 2、harbor1_获取 docker compose 二进制文件2_获取harbor安装文件3_获取TLS文件4_修改配置文件5_执行预备脚本6_执行安装脚本7_验证运行情况8_访问harborUI界面9_har…

概率论相关知识随记

作为基础知识的补充&#xff0c;随学随记&#xff0c;方便以后查阅。 概率论相关知识随记 期望&#xff08;Expectation&#xff09;期望的定义离散型随机变量的期望示例&#xff1a;掷骰子的期望 连续型随机变量的期望示例&#xff1a;均匀分布的期望 期望的性质线性性质期望的…

如何解决压测过程中JMeter堆内存溢出问题

如何解决压测过程中JMeter堆内存溢出问题 背景一、为什么会堆内存溢出&#xff1f;二、解决堆内存溢出措施三、堆内存参数应该怎么调整&#xff1f;四、堆内存大小配置建议 背景 Windows环境下使用JMeter压测运行一段时间后&#xff0c;JMeter日志窗口报错“java.lang.OutOfMe…

快速了解 Aurora DSQL

上周在 AWS re:Invent大会&#xff08;类似于阿里云的云栖大会&#xff09;上推出了新的产品 Aurora DSQL[1] &#xff0c;在数据库层面提供了多区域、多点一致性写入的能力&#xff0c;兼容 PostgreSQL。并声称&#xff0c;在多语句跨区域的场景下&#xff0c;延迟只有Google …

java垃圾回收机制中的引用计数算法

垃圾回收机制 java 语言中一个显著的特点就是引入了java回收机制&#xff0c;是c程序Q员最头疼的内存管理的问题迎刃而解&#xff0c;它使得java程序员在编写程序的时候不在考虑内存管理。由于有个垃圾回收机制&#xff0c;iava中的额对象不在有“作用域”的概念&#xff0c;只…

android studio 读写文件操作(应用场景二)

android studio版本&#xff1a;2023.3.1 patch2 例程&#xff1a;readtextviewIDsaveandread 本例程是个过渡例程&#xff0c;如果单是实现下图的目的有更简单的方法&#xff0c;但这个方法是下一步工作的基础&#xff0c;所以一定要做。 例程功能&#xff1a;将两个textvi…

单片机C51--笔记8-STC89C51RC/RD-IIC协议

一、概述 IIC全称Inter-Integrated Circuit (集成电路总线) 是由PHILIPS公司在80年代开发的两线式串行总线&#xff0c;用于连接微控制器及其外围设备。IIC属于半双 工同步通信方式。 特点 简单性和有效性。 由于接口直接在组件之上&#xff0c;因此IIC总线占用的空间非常小…

小程序-基于java+SpringBoot+Vue的智慧校园管理系统设计与实现

项目运行 1.运行环境&#xff1a;最好是java jdk 1.8&#xff0c;我们在这个平台上运行的。其他版本理论上也可以。 2.IDE环境&#xff1a;IDEA&#xff0c;Eclipse,Myeclipse都可以。推荐IDEA; 3.tomcat环境&#xff1a;Tomcat 7.x,8.x,9.x版本均可 4.硬件环境&#xff1a…

Linux下的编程

实验7 Linux下的编程 一、实验目的 熟练掌握Linux下Python编程的方法、函数调用方法以及shell编程中的控制结构。 二、实验环境 硬件&#xff1a;PC电脑一台&#xff0c;网络正常。 配置&#xff1a;win10系统&#xff0c;内存大于8G &#xff0c;硬盘500G及以上。 软件&a…

flink的安装配置(详细版本)

Standalone集群模式安装部署 conda deactivate 退出 base环境 Flink支持多种安装模式。 local&#xff08;本地&#xff09;——本地模式 standalone——独立模式&#xff0c;Flink自带集群&#xff0c;开发测试环境使用 standaloneHA—独立集群高可用模式&#xff0c;Fli…

【大语言模型】LangChain ModelsIO与Models I/O Promopts详解

【大语言模型】LangChain ModelsIO与Prompts详解 一、LangChain ModelsIO1、简介2、Models I/O 的应用场景3、Models I/O 主要模块3.1、Prompts3.2、Modelsa、MESSAGES 类型 3.3、Output Parsers 二、LangChain ModelsIO Prompts1、简介2、Prompts 的优点3、实战示例3.1、Promp…