当前位置: 首页 > news >正文

双指针算法详解

目录

一、双指针

 二、双指针题目

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

相关文章:

双指针算法详解

目录 一、双指针 二、双指针题目 1.移动零 解法&#xff1a; 代码&#xff1a; 2.复写零 ​编辑 解法&#xff1a; 代码&#xff1a; 边界情况处理: 3.快乐数 ​编辑 解法:快慢指针 代码&#xff1a; 4.盛水最多的容器 解法&#xff1a;&#xff08;对撞指针&#xff09;…...

MySQL的最左匹配原则是什么

最左匹配原则是应用于联合索引的规则。 对于以下表F&#xff1a;f1&#xff0c;f2&#xff0c;f3&#xff1b;建立了联合索引&#xff08;f2&#xff0c;f3&#xff09;&#xff0c;那么我们在查询的时候如果是&#xff1a; select * from F where f2 ? and f3 ?; 或 sele…...

LeetCode:106.从中序与后序遍历序列构造二叉树

跟着carl学算法&#xff0c;本系列博客仅做个人记录&#xff0c;建议大家都去看carl本人的博客&#xff0c;写的真的很好的&#xff01; 代码随想录 LeetCode&#xff1a;106.从中序与后序遍历序列构造二叉树 给定两个整数数组 inorder 和 postorder &#xff0c;其中 inorder …...

22. 【.NET 8 实战--孢子记账--从单体到微服务】--记账模块--切换主币种

这篇文章我们将结合主币种设置以及收支记录实现切换主币种后重新计算以前记录的转换后的金额。那么&#xff0c;为什么要在切换主币种后要重新计算转换后的金额呢&#xff1f;有以下两个原因&#xff1a; 统一的币种&#xff0c;方便我们统计数据方便用户按照当地的币种查看收…...

01.02周四F34-Day43打卡

文章目录 1. 地是湿的。昨晚估计下雨了。2. 你可能把包丢在餐厅里了吧?3. 她说他可能误了航班。4. 我本来应该早点来的,但路上特别堵。5. 约翰可能在那次事故中受了重伤。6. 这是一个情景对话7. 我本可以走另一条路的。8. 我准是瘦了不少,你看我这裤子现在多肥。9. 钱没了!会…...

行业商机信息付费小程序系统开发方案

行业商机信息付费小程序系统&#xff0c;主要是整合优质行业资源&#xff0c;实时更新的商机信息。在当今信息爆炸的时代&#xff0c;精准、高效地获取行业商机信息对于企业和个人创业者而言至关重要。 一、使用场景 日常浏览&#xff1a;用户在工作间隙或闲暇时间&#xff0c…...

cut-命令详解

一、命令 1.cut列截取命令 cut命令的默认分隔符是制表符 2.参数&#xff1a; -f 列号 #提取第几列-d 分隔符 #按照指定分隔符分割列-c 字符范围 #不依赖分隔符来区分列&#xff0c;而是通过字符范围&#xff08;行首为0&#xff09;来进行字段提取。“n-”表…...

Apache MINA 反序列化漏洞CVE-2024-52046

漏洞描述&#xff1a; Apache MINA 是一个功能强大、灵活且高性能的网络应用框架。它通过抽象网络层的复杂性&#xff0c;提供了事件驱动架构和灵活的 Filter 链机制&#xff0c;使得开发者可以更容易地开发各种类型的网络应用。 Apache MINA 框架的 ObjectSerializationDeco…...

二、AI知识(神经网络)

二、AI知识&#xff08;神经网络&#xff09; 1.常用算法 FNN CNN RNN LSTM DNN GRU 2.深度学习中概念及算法 1. 感知机 感知机&#xff08;Perceptron&#xff09;是一种最早的人工神经网络模型之一&#xff0c;通常用来解决二分类问题。它由弗兰克罗森布拉特&#…...

node.js之---子线程(child_process)模块

为什么需要子线程&#xff08;child_process&#xff09;模块 Worker Threads 的基本概念 如何使用 Worker Threads Worker Threads 的性能 Worker 线程的优势和限制 进阶用法&#xff1a;共享内存 为什么需要子线程&#xff08;child_process&#xff09;模块 在 Node.js…...

Json字符串解析失败

通过第三方服务&#xff0c;拿到响应体的data对象&#xff08;拿到的时候对象是有值的&#xff09; 通过JSON.parseObject方法&#xff0c;拿到的对象&#xff0c;值为null 通过查看对应的json字符串&#xff0c;发现命名不一样... JSONField SeriealizedName注解是用来解析j…...

LeetCode算法题——螺旋矩阵ll

题目描述 给你一个正整数n&#xff0c;生成一个包含1到n2所有元素&#xff0c;且元素按顺时针顺序螺旋排列的n x n正方形矩阵matrix 。 示例 输入&#xff1a;n 3 输出&#xff1a;[[1,2,3],[8,9,4],[7,6,5]]题解 思路&#xff1a; 将整个过程分解为逐圈填充的过程&#xf…...

【开源社区openEuler实践】hpcrunner

title: 探索 Hpcrunner&#xff1a;高性能计算的得力助手 date: ‘2024-12-31’ category: blog tags: Hpcrunner高性能计算任务调度资源优化 sig: HPC archives: ‘2024-12’ author:way_back summary: Hpcrunner 作为高性能计算领域的一款实用工具&#xff0c;专注于优化任务…...

linux下安装达梦数据库v8详解

目录 操作系统、数据库 1、下载达梦数据库 2、安装前准备 2.1、建立数据库用户和组 2.2、修改文件打开最大数 2.3、挂载镜像 2.4、新建安装目录 3、数据库安装 4、配置环境变量 5、初始化数据库实例 6、注册服务 7、使用数据库 8、卸载数据库 9、多实例管理 10、…...

Redis的常用命令

Redis中文字典网站 redis 命令手册https://redis.com.cn/commands.html Keys * 查看当前库所有的key exists ke 判断某个key是否存在 type key查看你的key是什么类型 Del key删除执行的key数据 unlink key非阻塞删除&#xff0c;仅仅将keys从keyspace元数据中删除&#xf…...

Docker入门常用命令总结

1.从远程仓库拉取一个纯净的镜像 docker pull docker .io/centos 2.创建并进入容器&#xff08;左外右内&#xff09; docker run --name xxx -dit 镜像id&#xff08;镜像名称:Tag&#xff09; /bin/bash 【参数必须放在镜像ID之前】 -i 让Docker分配一个伪终端&#xff0c;并…...

【Qt】容器控件、布局管理控件

目录 容器控件 QGroupBox QTabWidget 布局管理控件 QVBoxLayout 例子&#xff1a; QHBoxLayout 例子&#xff1a; QGridLayout 例子&#xff1a; 例子&#xff1a; QFormLayout 例子&#xff1a; QSpacerItem 例子&#xff1a; 容器控件 QGroupBox 表示一个带有…...

cesium小知识:常见的20多种property详解

要详细解释 Cesium 中所有的 Property 类,内容确实会非常丰富且详尽。 Property 基础 Property 是 Cesium 中用于表示随时间或条件变化的值的基础类。它允许你定义属性值如何根据时间、用户交互或其他逻辑动态改变。Property 的设计使得你可以创建复杂的动画和交互效果,而…...

图数据库 | 17、高可用分布式设计(上)

我们在前面的文章中&#xff0c;探索了多种可能的系统扩展方式&#xff0c;以及每种扩展方式的优劣。 本篇文章将通过具体的架构设计方案来对每一种方案的设计、投入产出比、各项指标与功能&#xff0c;以及孰优孰劣等进行评价。 在设计高性能、高可用图数据库的时候&#xf…...

1.运控概述

以下并不是我原创&#xff08;包括图片&#xff09;&#xff0c;都是来源于网络收集。如CSDN博主&#xff0c;朝夕教育&#xff0c;AI等。 什么是运动控制 运控是指“控制移动”之意&#xff0c;可以利用各种电机进行位置控制等操作&#xff0c;让机器听懂你的指令。 什么是…...

Leetcode 3576. Transform Array to All Equal Elements

Leetcode 3576. Transform Array to All Equal Elements 1. 解题思路2. 代码实现 题目链接&#xff1a;3576. Transform Array to All Equal Elements 1. 解题思路 这一题思路上就是分别考察一下是否能将其转化为全1或者全-1数组即可。 至于每一种情况是否可以达到&#xf…...

uni-app学习笔记二十二---使用vite.config.js全局导入常用依赖

在前面的练习中&#xff0c;每个页面需要使用ref&#xff0c;onShow等生命周期钩子函数时都需要像下面这样导入 import {onMounted, ref} from "vue" 如果不想每个页面都导入&#xff0c;需要使用node.js命令npm安装unplugin-auto-import npm install unplugin-au…...

高频面试之3Zookeeper

高频面试之3Zookeeper 文章目录 高频面试之3Zookeeper3.1 常用命令3.2 选举机制3.3 Zookeeper符合法则中哪两个&#xff1f;3.4 Zookeeper脑裂3.5 Zookeeper用来干嘛了 3.1 常用命令 ls、get、create、delete、deleteall3.2 选举机制 半数机制&#xff08;过半机制&#xff0…...

Spring AI 入门:Java 开发者的生成式 AI 实践之路

一、Spring AI 简介 在人工智能技术快速迭代的今天&#xff0c;Spring AI 作为 Spring 生态系统的新生力量&#xff0c;正在成为 Java 开发者拥抱生成式 AI 的最佳选择。该框架通过模块化设计实现了与主流 AI 服务&#xff08;如 OpenAI、Anthropic&#xff09;的无缝对接&…...

IoT/HCIP实验-3/LiteOS操作系统内核实验(任务、内存、信号量、CMSIS..)

文章目录 概述HelloWorld 工程C/C配置编译器主配置Makefile脚本烧录器主配置运行结果程序调用栈 任务管理实验实验结果osal 系统适配层osal_task_create 其他实验实验源码内存管理实验互斥锁实验信号量实验 CMISIS接口实验还是得JlINKCMSIS 简介LiteOS->CMSIS任务间消息交互…...

如何理解 IP 数据报中的 TTL?

目录 前言理解 前言 面试灵魂一问&#xff1a;说说对 IP 数据报中 TTL 的理解&#xff1f;我们都知道&#xff0c;IP 数据报由首部和数据两部分组成&#xff0c;首部又分为两部分&#xff1a;固定部分和可变部分&#xff0c;共占 20 字节&#xff0c;而即将讨论的 TTL 就位于首…...

nnUNet V2修改网络——暴力替换网络为UNet++

更换前,要用nnUNet V2跑通所用数据集,证明nnUNet V2、数据集、运行环境等没有问题 阅读nnU-Net V2 的 U-Net结构,初步了解要修改的网络,知己知彼,修改起来才能游刃有余。 U-Net存在两个局限,一是网络的最佳深度因应用场景而异,这取决于任务的难度和可用于训练的标注数…...

虚幻基础:角色旋转

能帮到你的话&#xff0c;就给个赞吧 &#x1f618; 文章目录 移动组件使用控制器所需旋转&#xff1a;组件 使用 控制器旋转将旋转朝向运动&#xff1a;组件 使用 移动方向旋转 控制器旋转和移动旋转 缺点移动旋转&#xff1a;必须移动才能旋转&#xff0c;不移动不旋转控制器…...

C++中vector类型的介绍和使用

文章目录 一、vector 类型的简介1.1 基本介绍1.2 常见用法示例1.3 常见成员函数简表 二、vector 数据的插入2.1 push_back() —— 在尾部插入一个元素2.2 emplace_back() —— 在尾部“就地”构造对象2.3 insert() —— 在任意位置插入一个或多个元素2.4 emplace() —— 在任意…...

基于小程序老人监护管理系统源码数据库文档

摘 要 近年来&#xff0c;随着我国人口老龄化问题日益严重&#xff0c;独居和居住养老机构的的老年人数量越来越多。而随着老年人数量的逐步增长&#xff0c;随之而来的是日益突出的老年人问题&#xff0c;尤其是老年人的健康问题&#xff0c;尤其是老年人产生健康问题后&…...