双指针算法详解

目录

一、双指针

 二、双指针题目

 1.移动零

 解法:

代码:

 2.复写零

​编辑

 解法:

 代码:

边界情况处理:

 3.快乐数

​编辑

 解法:快慢指针

代码:

4.盛水最多的容器

 解法:(对撞指针)

代码: 

 5.有效三角形的个数

​编辑  

解法:

 代码:

6.和为s的两个数字

 解法:(对撞指针)

 代码:

7.三数之和

 解法:

 代码:


一、双指针

常⻅的双指针有两种形式,⼀种是对撞指针,⼀种是左右指针。
对撞指针:⼀般⽤于顺序结构中,也称左右指针。
  1. 对撞指针从两端向中间移动。⼀个指针从最左端开始,另⼀个从最右端开始,然后逐渐往中间逼近。
  2. 对撞指针的终⽌条件⼀般是两个指针相遇或者错开(也可能在循环内部找到结果直接跳出循环),也就是:
  • left == right (两个指针指向同⼀个位置)
  • left > right (两个指针错开)
快慢指针:⼜称为⻳兔赛跑算法,其基本思想就是使⽤两个移动速度不同的指针在数组或链表等序列结构上移动。
这种⽅法对于处理环形链表或数组⾮常有⽤。
其实不单单是环形链表或者是数组,如果我们要研究的问题出现循环往复的情况时,均可考虑使⽤快 慢指针的思想。
快慢指针的实现⽅式有很多种,最常⽤的⼀种就是:
  • 在⼀次循环中,每次让慢的指针向后移动⼀位,⽽快的指针往后移动两位,实现⼀快⼀慢。

 二、双指针题目

 1.移动零

【数组分两块】是⾮常常⻅的⼀种题型,主要就是根据⼀种划分⽅式,将数组的内容分成左右两部
分。这种类型的题,⼀般就是使⽤「双指针」来解决。

 283. 移动零 - 力扣(LeetCode)

 解法:

两个指针:

  • cur:从左到右扫描整个数组
  • dest:已处理的数组中,非零元素的最后位置

代码:

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]);//交换,dest++
    }
};

 swap(nums[++dest], nums[cur]):这里的操作有两部分:

  1. ++dest:先将 dest 指针向前移动一位。这个步骤确保了每当找到一个非零元素时,它会被放置到数组的前面,而 dest 会保持在下一个非零元素的位置。
  2. swap(nums[dest], nums[cur]):将 cur 指向的非零元素与 dest 指向的元素交换位置。此时,cur 指向的非零元素被放到数组的前面,dest 也向前移动一位,以准备接收下一个非零元素。

 2.复写零

1089. 复写零 - 力扣(LeetCode)

 解法:

从后往前复写,大体流程分为两步:

  1. 先找到最后一个复写的数
    先判断cur位置的值
    决定dest向后移动一步或者两步
    判断一下dest是否已经到结束为止
    cur++
  2. 从后往前进行复写操作

 代码:

class Solution {
public:
    void duplicateZeros(vector<int>& arr) {
        //1.先找到最后一个数
        int cur=0,dest=-1,n=arr.size();
        while(cur<n)
        {
            if(arr[cur]) dest++;//不等于0,走1步
            else dest+=2;//等于0,走两步
            if(dest >= n-1) break;
            cur++;
        }
        //2.处理一下边界情况(最后一个元素是0,需要复写两遍,会导致越界;)
        if(dest == n)//判断越界
        {
            arr[n - 1]=0;
            cur--;dest-=2;
        }
        //3.从后向前完成复写操作
        while(cur >= 0)
        {
            if(arr[cur]) arr[dest--]=arr[cur--];
            else
            {
                arr[dest--]=0;
                arr[dest--]=0;
                cur--;
            }
        }
    }
};

边界情况处理:

如果越界:1.n-1位置的值修改成0;

                  2.cur向前移动一步

                  3.dest向前移动两步

 3.快乐数

 202. 快乐数 - 力扣(LeetCode)

 解法:快慢指针

【快慢指针】有⼀个特性,就是在⼀个圆圈中,快指针总是会追上慢指针的,也就是说他们总会相遇在⼀个位置上。如果相遇位置的值是 1 ,那么这个数⼀定是快乐数;如果相遇位置不是 1的话,那么就不是快乐
  1. 定义快慢指针
  2. 满指针每次向后移动一步,块指针每次向后移动两步
  3. 判断相遇时候的值即可

 补充:求一个数n每个位置上的数组的平方和

1.把数 n 每⼀位的数提取出来:
        循环迭代下⾯步骤:
  • int t = n % 10 提取个位;
  • n /= 10 ⼲掉个位;
直到 n 的值变为 0
2.提 取每⼀位的时候,⽤⼀个变量 tmp 记录这⼀位的平⽅与之前提取位数的平⽅和
  • tmp = tmp + t * t

代码:

class Solution {
public:
    int Sum(int n)
    {
        int sum=0;
        while(n)
        {
            int t=n%10;
            sum+=t*t;
            n/=10;
        }
        return sum;
    }
    bool isHappy(int n) {
        //双指针,solw走一步,fast走两步
        int slow=n,fast=Sum(n);
        while(slow!=fast)
        {
            slow=Sum(slow);
            fast=Sum(Sum(fast));
        }
        return slow==1;
    }
};

4.盛水最多的容器

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

 解法:(对撞指针)

设两个指针 left right 分别指向容器的左右两个端点,此时容器的容积 :
v = (right - left) * min( height[right], height[left])
容器的左边界为 height[left] ,右边界为 height[right]
为了⽅便叙述,我们假设「左边边界」⼩于「右边边界」。
如果此时我们固定⼀个边界,改变另⼀个边界,⽔的容积会有如下变化形式:
  • 容器的宽度⼀定变⼩。
  • 由于左边界较⼩,决定了⽔的⾼度。如果改变左边界,新的⽔⾯⾼度不确定,但是⼀定不会超 过右边的柱⼦⾼度,因此容器的容积可能会增⼤。
  • 如果改变右边界,⽆论右边界移动到哪⾥,新的⽔⾯的⾼度⼀定不会超过左边界,也就是不会 超过现在的⽔⾯⾼度,但是由于容器的宽度减⼩,因此容器的容积⼀定会变⼩的。
由此可⻅,左边界和其余边界的组合情况都可以舍去。所以我们可以 left++ 跳过这个边界,继续去判断下⼀个左右边界。
当我们不断重复上述过程,每次都可以舍去⼤量不必要的枚举过程,直到 left right
遇。期间产⽣的所有的容积⾥⾯的最⼤值,就是最终答案

代码: 

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

 5.有效三角形的个数

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

  

解法:

先将数组排序

双指针法

i = n-1 开始,遍历每个可能的第三条边(从最大的边开始)。然后使用两个指针 leftright,分别指向数组的起始和 i-1 位置。

  • 如果 nums[left] + nums[right] > nums[i],说明当前的 left 和 right 可以和 nums[i] 组成一个三角形,并且 [left, right-1] 之间的所有组合也满足条件。此时,结果增加 right - left
  • 如果不满足条件,说明 nums[left] 太小,无法与 nums[right] 和 nums[i] 组成三角形,需要增大 left

 代码:

class Solution {
public:
    int triangleNumber(vector<int>& nums) {
        sort(nums.begin(),nums.end());
        int n=nums.size(),ret=0;
        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;
                    //如果nums[left]+nums[right]>nums[i]说明[left,right-1]区间上的
                    //所有元素均可以与nums[right]构成比nums[i]大的二元组
                    //有right-left种
                    right--;
                }
                else
                //nums[left] + nums[right] <= nums[i]说明left位置的元素是不可能与[left+1,right]位置上的元素构成满足条件的二元组
                {
                    left++;
                }
            }
        }
        return ret;
    }
};

6.和为s的两个数字

 

 解法:(对撞指针)

  1. 初始化左右指针left = 0right = price.size() - 1left 指向数组的起始位置,right 指向数组的末尾位置。

  2. 循环条件while (left < right)。这表示只要 left 小于 right,就继续进行搜索。如果 left >= right,说明已经遍历完了所有可能的组合。

  3. 计算当前和int sum = price[left] + price[right]。计算当前指针所指向的两个元素的和。

  4. 判断和的关系

    • 如果和大于目标值sum > target,说明当前两个数的和太大了,可以将 right 指针左移,减小和。
    • 如果和小于目标值sum < target,说明当前两个数的和太小了,可以将 left 指针右移,增大和。
    • 如果和等于目标值sum == target,找到目标和,直接返回这两个元素。

 代码:

class Solution {
public:
    vector<int> twoSum(vector<int>& price, int target) {
        int left=0,right=price.size()-1;
        while(left<right)
        {
            int sum=price[left]+price[right];
            if(sum>target) right--;//当 nums[left] + nums[right] > target 时,nums[right]与最小的数相加还大于target,剩下的也肯定大于,直接right--
            else if(sum<target) left++;//同理,当 nums[left] + nums[right] > target 时,直接left++
            else return {price[left],price[right]};
        }
        return {-1,-1};
    }
};

7.三数之和

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

 解法:

利⽤在两数之和那⾥⽤的双指针思想:
  • 先排序;
  • 然后固定⼀个数 a
  • 在这个数后⾯的区间内,使⽤「双指针算法」快速找到两个数之和等于 -a 即可。
  • 但是要注意的是,这道题⾥⾯需要有「去重」操作
    1.找到⼀个结果之后, left right 指针要「跳过重复」的元素;
    2.当使⽤完⼀次双指针算法之后,固定的 a 也要「跳过重复」的元素

 代码:

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        vector<vector<int>> ret;
        //1.排序
        sort(nums.begin(),nums.end());
        //2.利用双指针解决问题
        int n=nums.size();
        for(int i=0;i<n;)
        {
            if(nums[i]>0) break;
            int left=i+1,right=n-1,target=-nums[i];
            while(left<right)
            {
                int sum=nums[left]+nums[right];
                if(sum>target)right--;
                else if(sum<target)left++;
                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;
    }
};
感谢,再见

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

每天40分玩转Django:Django Celery

Django Celery 一、知识要点概览表 模块知识点掌握程度要求Celery基础配置、任务定义、任务执行深入理解异步任务任务状态、结果存储、错误处理熟练应用周期任务定时任务、Crontab、任务调度熟练应用监控管理Flower、任务监控、性能优化理解应用 二、基础配置实现 1. 安装和…

Web安全扫盲

1、建立网络思维模型的必要 1 . 我们只有知道了通信原理&#xff0c; 才能够清楚的知道数据的交换过程。 2 . 我们只有知道了网络架构&#xff0c; 才能够清楚的、准确的寻找漏洞。 2、局域网的简单通信 局域网的简单通信&#xff08;数据链路层&#xff09; 一般局域网都通…

【MATLAB APP Designer】小波阈值去噪(第一期)

代码原理及流程 小波阈值去噪是一种信号处理方法&#xff0c;用于从信号中去除噪声。这种方法基于小波变换&#xff0c;它通过将信号分解到不同的尺度和频率上来实现。其基本原理可以分为以下几个步骤&#xff1a; &#xff08;1&#xff09;小波变换&#xff1a;首先对含噪信…

CDP集群安全指南-动态数据加密

[〇]关于本文 集群的动态数据加密主要指的是加密通过网络协议传输的数据&#xff0c;防止数据在传输的过程中被窃取。由于大数据涉及的主机及服务众多。你需要更具集群的实际环境来评估需要为哪些环节实施动态加密。 这里介绍一种通过Cloudera Manager 的Auto-TLS功能来为整个…

信息安全、网络安全和数据安全的区别和联系

1. 前言 有次有朋友问我 信息安全、网络安全和数据安全&#xff0c;这三个词平时写文档时怎么用&#xff1f; 我想很多人都说不清。这次我查阅了资料&#xff0c;尽量讲清楚这三者之间的区别和联系。 2. 信息安全 2.1 定义 信息安全是指为数据处理系统建立和采用的技术和管…

vim 的基础使用

目录 一&#xff1a;vim 介绍二&#xff1a;vim 特点三&#xff1a;vim 配置四&#xff1a;vim 使用1、vim 语法格式2、vim 普通模式&#xff08;1&#xff09;保存退出&#xff08;2&#xff09;光标跳转&#xff08;3&#xff09;文本删除&#xff08;4&#xff09;文本查找&…

Unity2022接入Google广告与支付SDK、导出工程到Android Studio使用JDK17进行打包完整流程与过程中的相关错误及处理经验总结

注&#xff1a;因为本人也是第一次接入广告与支付SDK相关的操作&#xff0c;网上也查了很多教程&#xff0c;很多也都是只言片语或者缺少一些关键步骤的说明&#xff0c;导致本人也是花了很多时间与精力踩了很多的坑才搞定&#xff0c;发出来也是希望能帮助到其他人在遇到相似问…

【嵌入式硬件】直流电机驱动相关

项目场景&#xff1a; 驱动履带车&#xff08;双直流电机&#xff09;前进、后退、转弯 问题描述 电机驱动MOS管烧毁 电机驱动采用IR2104STRH1R403NL的H桥方案&#xff08;这是修改之后的图&#xff09; 原因分析&#xff1a; 1.主要原因是4路PWM没有限幅&#xff0c;修改…

数据库知识汇总1

一. 数据库系统概述 信息需要媒体&#xff08;文本、图像视频等&#xff09;表现出来才能被人类所获取&#xff0c;媒体可以转换成比特或者符号&#xff0c;这些称为数据&#xff1b; 数据/信息的特点&#xff1a;爆炸式增长、无限复制、派生&#xff1b; 数据库是指长期长期…

Dubbo扩展点加载机制

加载机制中已经存在的一些关键注解&#xff0c;如SPI、©Adaptive> ©Activateo然后介绍整个加载机制中最核心的ExtensionLoader的工作流程及实现原理。最后介绍扩展中使用的类动态编译的实 现原理。 Java SPI Java 5 中的服务提供商https://docs.oracle.com/jav…

Elasticsearch向量检索需要的数据集以及768维向量生成

Elasticsearch8.17.0在mac上的安装 Kibana8.17.0在mac上的安装 Elasticsearch检索方案之一&#xff1a;使用fromsize实现分页 快速掌握Elasticsearch检索之二&#xff1a;滚动查询(scrool)获取全量数据(golang) Elasticsearch检索之三&#xff1a;官方推荐方案search_after…

网关的主要作用

在网络安全领域&#xff0c;网关扮演着举足轻重的角色&#xff0c;它不仅是网络间的桥梁&#xff0c;更是安全防线的守护者。以下是网关在网络安全中的几个关键作用&#xff1a; 1. 防火墙功能&#xff1a;网关常常集成了防火墙技术&#xff0c;能够对进出网络的数据包进行严格…

【Cocos TypeScript 零基础 4.1】

目录 背景滚动 背景滚动 创建一个 空节点 背景丟进去 ( 复制一个,再丢一次都行) 新建TS脚本 并绑定到 空节点 上 再对TS脚本进行编辑 export class TS2bg extends Component {property (Node) // 通过属性面板去赋值bg1:Node nullproperty (Node) bg2:Node nullprope…

如何利用群晖NAS实现远程访问你的网页版Linux虚拟桌面环境

文章目录 前言1. 下载Docker-Webtop镜像2. 运行Docker-Webtop镜像3. 本地访问网页版Linux系统4. 群晖NAS安装Cpolar工具5. 配置异地访问Linux系统6. 异地远程访问Linux系统7. 固定异地访问的公网地址 前言 今天我要给大家介绍一下如何在群晖NAS设备上部署Docker-Webtop&#x…

MySQL 04 章——运算符

一、算数运算符 算数运算符主要用于数学运算&#xff0c;其可以连接运算符前后的两个数值或表达式 运算符名称作用示例加法运算符计算两个值或表达式的和SELECT AB-减法运算符计算两个值或表达式的差SELECT A-B*乘法运算符计算两个值或表达式的乘积SELECT A*B/或DIV除法运算符…

ES IK分词器插件

前言 ES中默认了许多分词器&#xff0c;但是对中文的支持并不友好,IK分词器是一个专门为中文文本设计的分词工具&#xff0c;它不是ES的内置组件&#xff0c;而是一个需要单独安装和配置的插件。 Ik分词器的下载安装&#xff08;Winows 版本&#xff09; 下载地址&#xff1a;…

Oracle DG备库数据文件损坏修复方法(ORA-01578/ORA-01110)

今天负责报表的同事反馈在DG库查询时出现如下报错 ORA-01578:ORACLE数据块损坏(文件号6,块号 2494856)ORA-01110:数据文件6: /oradata/PMSDG/o1 mf users_molczgmn_.dbfORA-26040:数据块是使用 NOLOGGING 选项加载的 可以看到报错是数据文件损坏&#xff0c;提示了file id和b…

idea无法安装插件

目录 修改工具配置 本地安装 无法下载很多时候就是延迟太高导致的&#xff0c;我们先打开插件官网看一下 Python - IntelliJ IDEs Plugin | Marketplace 修改工具配置 1、配置代理&#xff08;点击 setting-点击 plugins-在点击 http proxy Settings&#xff09; 输入&…

Linux部署web项目【保姆级别详解,Ubuntu,mysql8.0,tomcat9,jdk8 附有图文】

文章目录 部署项目一.安装jdk1.1 官网下载jdk81.2 上传到Linux1.3 解压1.4 配置环境变量1.5 查看是jdk是否安装成功 二.安装TomCat2.1 官网下载2.2 上传到Linux2.3 解压2.4配置2.5 启动Tomcat2.6 验证是否成功 三.安装mysql四.部署javaweb项目4.1 打包4.2 启动tomcat 部署项目 …

服务器等保测评日志策略配置

操作系统日志 /var/log/message 系统启动后的信息和错误日志&#xff0c;是Red Hat Linux中最常用的日志之一 /var/log/secure 与安全相关的日志信息 /var/log/maillog 与邮件相关的日志信息 /var/log/cron 与定时任务相关的日志信息 /var/log/spooler 与UUCP和news设备相关的…