双指针算法详解

目录

一、双指针

 二、双指针题目

 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;}
};
感谢,再见

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【文献精读笔记】Explainability for Large Language Models: A Survey (大语言模型的可解释性综述)(三)

****非斜体正文为原文献内容&#xff08;也包含笔者的补充&#xff09;&#xff0c;灰色块中是对文章细节的进一步详细解释&#xff01; 3.2 全局解释&#xff08;Global Explanation&#xff09; 与旨在解释模型个体预测的局部解释不同&#xff0c;全局解释提供了对语言模型…

STM32G431收发CAN

1.硬件连接 PB8作为CAN_RX&#xff0c;PB9作为CAN_TX&#xff0c;连接一个CAN收发器TJA1051T/3 2. CubeMX里配置CAN 设置连接FDCAN1的参数&#xff0c;使用1个标准过滤器&#xff0c;波特率位500K 使能FDCAN1的中断 3 自动生成代码 3.1 初始化 static void MX_FDCAN1_In…

设计心得——流程图和数据流图绘制

一、流程图和数据流图 在软件开发中&#xff0c;画流程图和数据流图可以说是几乎每个人都会遇到。 1、数据流&#xff08;程&#xff09;图 Data Flow Diagram&#xff0c;DFG。它可以称为数据流图或数据流程图。其主要用来描述系统中数据流程的一种图形工具&#xff0c;可以将…

普及组集训数据结构--并查集

P1551 亲戚 - 洛谷 | 计算机科学教育新生态 并查集就是把所有相关联的量串成一串珠子&#xff0c;抽象来说就是&#xff1a; 把此类相关联的量当作节点&#xff0c;两个节点之间连接一条无向边&#xff0c;所形成的图 例题算法流程&#xff1a; 在此定义“族长”就是一个树的…

路由基本配置实验

路由器用于实现不同类型网络之间的互联。 路由器转发ip分组的基础是路由表。 路由表中的路由项分为直连路由项、静态路由项和动态路由项。 通过配置路由器接口的ip地址和子网掩码自动生成直连路由项。 通过手工配置创建静态路由项。 热备份路由器协议允许将由多个路由器组…

17爬虫:关于DrissionPage相关内容的学习01

概述 前面我们已经大致了解了selenium的用法&#xff0c;DerssionPage同selenium一样&#xff0c;也是一个基于Python的网页自动化工具。 DrissionPage既可以实现网页的自动化操作&#xff0c;也能够实现收发数据包&#xff0c;也可以把两者的功能合二为一。 DressionPage的…

计算机网络•自顶向下方法:网络层介绍、路由器的组成

网络层介绍 网络层服务&#xff1a;网络层为传输层提供主机到主机的通信服务 每一台主机和路由器都运行网络层协议 发送终端&#xff1a;将传输层报文段封装到网络层分组中&#xff0c;发送给边缘路由器路由器&#xff1a;将分组从输入链路转发到输出链路接收终端&#xff1…

下载linux aarch64版本的htop

htop代码网站似乎没有编译好的各平台的包&#xff0c;而自己编译需要下载一些工具&#xff0c;比较麻烦。这里找到了快速下载和使用的方法&#xff0c;记录一下。 先在linux电脑上执行&#xff1a; mkdir htop_exe cd htop_exe apt download htop:arm64 # 会直接下载到当前目…

呼叫中心中间件实现IVR进入排队,判断排队超时播放提示音

文章目录 [TOC](文章目录) 前言需求排队结束原因 联系我们实现步骤1. 调用http接口返回动作2. 启用拨号方案 前言 需求 呼叫中心需要实现调用IVR接口进入排队&#xff0c;如果是因为等待超时导致退出排队的&#xff0c;那就播放一段提示音再挂断通话&#xff1b;其他的情况就…

如何二次封装组件(vue3版本)

在开发 Vue 项目中我们一般使用第三方组件库进行开发&#xff0c;如 Element-Plus, 但是这些组件库提供的组件并不一定满足我们的需求&#xff0c;这时我们可以通过对组件库的组件进行二次封装&#xff0c;来满足我们特殊的需求。 对于封装组件有一个大原则就是我们应该尽量保…

【74HC192减法24/20/72进制】2022-5-17

缘由用74ls192设计一个72进制的减法计数器&#xff0c;需要有逻辑电路图-硬件开发-CSDN问答

Fastapi项目通过Jenkins2.4.91自动化构建部署到Nginx1.20进行访问详细方法(完全自动化部署亲测可用)

这篇技术文章需要结合我写的前两篇文章来一起看Gitlab17.7Jenkins2.4.91实现Fastapi/Django项目持续发布版本详细操作(亲测可用) 和 Pycharm2024.3Gitlab.17.7本地化部署和自动提交代码使用方法&#xff08;亲测可用&#xff09;&#xff0c;总体来说是三部曲。这篇文章详细解读…

iOS 11 中的 HEIF 图像格式 - 您需要了解的内容

HEIF&#xff0c;也称为高效图像格式&#xff0c;是iOS 11 之后发布的新图像格式&#xff0c;以能够在不压缩图像质量的情况下以较小尺寸保存照片而闻名。换句话说&#xff0c;HEIF 图像格式可以具有相同或更好的照片质量&#xff0c;同时比 JPEG、PNG、GIF、TIFF 占用更少的设…

DATACOM-DHCP-复习-实验

DHCP 概述工作原理DHCP分配机制 配置配置基于全局地址池的DHCP服务器配置DHCP Relay中继验证 实验配置DHCP中继 参考 概述 动态主机配置协议DHCP&#xff08;Dynamic Host Configuration Protocol&#xff09;是一种网络管理协议&#xff0c;用于集中对用户IP地址进行动态管理和…

深入浅出 Beam Search:自然语言处理中的高效搜索利器

Beam Search 技术详解 搜索系列相关文章&#xff08;置顶&#xff09; 1.原始信息再加工&#xff1a;一文读懂倒排索引 2.慧眼识词&#xff1a;解析TF-IDF工作原理 3.超越TF-IDF&#xff1a;信息检索之BM25 4.深入浅出 Beam Search&#xff1a;自然语言处理中的高效搜索利器 1…

二、CSS基础

一、选择器(1) 大白话&#xff1a;我们人为认为的解析方式是&#xff0c;从左往右查找&#xff0c;对于浏览器来说&#xff0c;是从右往左查找&#xff0c;解析速度更高。 注&#xff1a; 伪类选择器 - 作用于实际存在的元素&#xff0c;用于描述元素的某种特定状态或关系&…

从摩托罗拉手机打印短信的简单方法

昨天我试图从摩托罗拉智能手机上打印短信&#xff0c;但当我通过USB将手机连接到电脑时&#xff0c;我在电脑上找不到它们。由于我的手机内存已达到限制&#xff0c;并且我想保留短信的纸质版本&#xff0c;您能帮我将短信从摩托罗拉手机导出到计算机吗&#xff1f; 如您所知&…

Linux终端输入删除键backspace显示^H,输入上下左右键显示^A^B^C^D原理以及详细解决办法!

当我们装完Linux系统之后,我们可能会碰到按下删除键后出现^H这种情况。 同样,输入上下左右键显示^A^B^C^D这种情况。 这是为什么呢? 别急,后面我会说具体解决办法,先来看看这是为什么? 一、终端程序架构 首先,我们需要了解终端程序架构。 终端程序架构分为三层,分别…

ESP32 I2S音频总线学习笔记(一):初识I2S通信与配置基础

文章目录 简介为什么需要I2S&#xff1f;关于音频信号采样率分辨率音频声道 怎样使用I2S传输音频&#xff1f;位时钟BCLK字时钟WS串行数据SD I2S传输模型I2S通信格式I2S格式左对齐格式右对齐格式 i2s基本配置i2s 底层API加载I2S驱动设置I2S使用的引脚I2S读取数据I2S发送数据卸载…

JAVA:利用 Redis 实现每周热评的技术指南

1、简述 在现代应用中&#xff0c;尤其是社交媒体和内容平台&#xff0c;展示热门评论是常见的功能。我们可以通过 Redis 的高性能和丰富的数据结构&#xff0c;轻松实现每周热评功能。本文将详细介绍如何利用 Redis 实现每周热评&#xff0c;并列出完整的实现代码。 2、需求分…