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

算法知识-双指针

一.双指针

设置两个指针的技巧,说法很宽泛。

(1)有时候所谓双指针技巧,就是单纯代码过程用双指针的形式表达出来而已,没有单调性(贪心)方面的考虑(例题1,例题2)

(2)有时候双指针技巧包含单调性(贪心)方面的考虑,牵扯到可能性的取舍,对于分析能力的要求会变高,其实是先有思考和优化,然后代码变成了双指针的形式

(3)所以,双指针名词并不重要,分析题目单调性(贪心)方面的特征,这个能力才重要

常见双指针类型:

同向双指针

快慢双指针

从两头往中间的双指针

其他

二.例题

922. 按奇偶排序数组 II - 力扣(LeetCode)

我们安排两个指针,一个是奇数指针从下标1出发,一个是偶数指针从下标0出发,我们盯着数组最后一个位置,不断看最后一个位置是奇数还是偶数,如果是奇数就和奇数下标交换一下,之后odd+=2,如果是偶数就和偶数下标交换一下,之后even+=2,循环结束条件是even或者odd越界,说明有奇数/偶数位置已经安排妥当,那么整体就已经安排妥当。

总结:以数组最后一位去发货,不断让奇数位和偶数位去成长,谁先成长越界就说明谁已经安排妥当

class Solution { public: vector<int> sortArrayByParityII(vector<int>& nums) { int n=nums.size(); for(int even=0,odd=1;even<n&&odd<n;){ if(nums[n-1]&1){ swap(nums[n-1],nums[odd]); odd+=2; }else{ swap(nums[n-1],nums[even]); even+=2; } } return nums; } };

287. 寻找重复数 - 力扣(LeetCode)

我们可以把数组想象成链表的形式,那么i指向nums[i],如果有重复的数字,那么链表一定成环,可以使用快慢指针

补充:快慢指针

(1)快指针一次跳两步,慢指针一次跳一步

(2)快慢指针相遇

(3)快指针回到起点,慢指针留在原处,每人一次跳一步,直到相遇,就是环的入口

class Solution { public: int findDuplicate(vector<int>& nums) { int n=nums.size(); int slow=nums[0]; int fast=nums[nums[0]]; while(slow!=fast){ slow=nums[slow]; fast=nums[nums[fast]]; } fast=0; while(fast!=slow){ fast=nums[fast]; slow=nums[slow]; } return slow; } };

42. 接雨水 - 力扣(LeetCode)

首先我们分析题目,对于任意一个位置i它所能承接的雨水,由于木桶的短板效应,所以水的高度,加上自身高度不能超过,左右两侧最大值的最小值,即能承接min(lMax,rMax)-a[i],但是由于i位置可能比左右两侧最大值都高,会计算出负数,所以i位置能承接的雨水应该是max(min(lMax,rMax)-a[i],0),之后我们通过前缀最大值分别记录每个位置左侧和右侧的最大值

class Solution { public: int trap(vector<int> &height) { int n = height.size(); vector<int> lMax(n); vector<int> rMax(n); lMax[0] = height[0]; for (int i = 1; i < n; i++) { lMax[i] = max(lMax[i-1], height[i]); } rMax[n - 1] = height[n - 1]; for (int i = n -2; i >= 1; i--) { rMax[i] = max(rMax[i+1], height[i]); } int ans = 0; for (int i = 1; i < n-1; i++) { ans += max((min(lMax[i], rMax[i]) - height[i]), 0); } return ans; } };

双指针优化

我们可以设置两个指针,l,r从1和n-2出发,之后维护一个lMax,和一个rMax,如果lMax<=rMax,l左侧为lMax,而l右侧应该>=rMax,那么l位置的短板一定是lMax,否则lMax>rMax,r右侧为真实的rMax,而r左侧应该>=lMax,所以r位置的短板应该是rMax,左指针l非严格单调递增(仅右移、不回退),右指针r非严格单调递减(仅左移、不回退)

int trap(vector<int> &height) { int n = height.size(); int l=1,r=n-2,lMax=height[0],rMax=height[n-1]; int ans=0; while(l<=r){ if(lMax<=rMax){ ans+=max(lMax-height[l],0); lMax=max(lMax,height[l++]); }else{ ans+=max(rMax-height[r],0); rMax=max(rMax,height[r--]); } } return ans; }

881. 救生艇 - 力扣(LeetCode)

我们首先对原数组排序,之后设置两个指针,l,r分别在开头和结尾

(1)a[l]+a[r]<=limit

l和r一船,因为对于l来说已经是最值的选择了

l++,r--

(2)a[l]+a[r]>limit

那么r自己一船,因为l右侧都是比l大的,不会有能和r一船的了

r--

class Solution { public: int numRescueBoats(vector<int>& people, int limit) { sort(people.begin(),people.end()); int l=0,r=people.size()-1; int ans=0; int sum=0; while(l<=r){ sum=l==r?people[l]:people[l]+people[r]; if(sum<=limit){ l++; r--; }else{ r--; } ans++; } return ans; } };

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

题目分析:

设置两个指针l,r分别从开头和结尾出发,如果h[l]<=h[r],以h[l]为开头的最好的情况就是现在,当前边长最长,并且以h[l]为开头一定要<=h[l]。所以更新一下答案,l++

反之,如果h[l]>h[r],以h[r]作为结尾最好情况就是现在,以h[r]作为结尾一定要<=h[r],并且现在边长最大

所以就是谁小就以谁作边长,并且移动谁

class Solution { public: int maxArea(vector<int>& height) { int res = 0; int l = 0; int r = height.size() - 1; while (l < r) { int area = (r - l) * min(height[l], height[r]); res = max(res, area); if (height[l] <= height[r]) { l++; } else { r--; } } return res; } };

475. 供暖器 - 力扣(LeetCode)

题目分析:本道题目比较简单,先对两个数组进行排序,就是一个指针l1从房屋出发,一个指针l2从供暖器出发,之后我们循环比较l1房屋到l2+1供暖器的距离是否小于等于l1到l2的距离呢,是的话说明l2+1更好,l2++,之后当l2移动到最后说明已经没有更大的供暖期进行选择了,我们只能选择l2了。

注意:如果l2距房屋距离与l1距房屋距离相等l2一定要移动,因为供暖器数组可能存在两个相等的值,如果不移动将会永远无法移动l2导致后续计算错误

class Solution { public: int findRadius(vector<int>& houses, vector<int>& heaters) { sort(houses.begin(),houses.end()); sort(heaters.begin(),heaters.end()); int n=houses.size(),m=heaters.size(); int l1=0,l2=0; int diff1=0,diff2=0; int ans=0; for(int l1=0;l1<n;l1++){ if(l2==m-1){ ans=max(ans,abs(houses[l1]-heaters[l2])); continue; } while(l2<m-1&&abs(houses[l1]-heaters[l2+1]) <=abs(houses[l1]-heaters[l2])){ l2++; } ans=max(ans,abs(houses[l1]-heaters[l2])); } return ans; } };

41. 缺失的第一个正数 - 力扣(LeetCode)

该题目比较难想。

题目分析:

如果正常的话就应该是a[l]=l+1,那么一开始我们假设每一个位置都是这样的那么我们能收集1~n都会存在,所以我们一开始有一个左指针l从0出发,代表l左侧是放好的,r从下标n出发代表垃圾区域,有一下几种情况

(1)a[l]==l+1,正常放置l++

(2)垃圾数据:

a[l]>r,只能收集1~r,大于r一定是垃圾

a[l]<=l,小于等于l在l左侧已经收集好了,垃圾

a[a[l]-1]==a[l],如果a[l]在l+1到r之间,需要把a[l]放到对应的位置上,但是发现该位置已经有相同的数字了,垃圾

把a[l]交换到垃圾区,swap(nums[l],nums[--r])

(3)不是垃圾,也不是该位置的数字,需要给他交换到它的位置上去

swap(nums[l],nums[nums[l]-1])

class Solution { public: int firstMissingPositive(vector<int>& nums) { int l=0,r=nums.size(); while(l<r){ if(nums[l]==l+1){ l++; }else if(nums[l]>r||nums[l]<=l||nums[nums[l]-1]==nums[l]){ swap(nums[l],nums[--r]); }else{ swap(nums[l],nums[nums[l]-1]); } } return l+1; } };
http://www.jsqmd.com/news/497051/

相关文章:

  • 基于SAM2的眼动数据跟踪3——python转exe
  • 比迪丽角色生成实战案例:从‘a beautiful girl’到龙珠经典造型复刻
  • 如何将genact假活动生成器集成到自动化脚本:完整指南
  • FireRed-OCR Studio入门指南:OCR结果置信度阈值设定与人工复核策略
  • 嵌入式C开发三大核心架构:从能运行到高可用的实战指南
  • Android开发的定心丸-Android从底层到上层开发技巧经验汇总_上卷_助您不走弯路_快速前行!
  • 比迪丽AI绘画教程:如何用Inpainting修复生成中的局部瑕疵
  • Qwen3-ASR-0.6B内容审核应用:敏感词实时检测与高亮标记
  • FireRed-OCR Studio开源镜像部署:GPU显存优化与量化配置详解
  • OpenClaw官方下载替代:nanobot开源镜像+Qwen3-4B全栈部署教程(含日志排查)
  • 通义千问1.5-1.8B-GPTQ-Int4效果展示:中文逻辑推理、多轮对话与代码生成真实案例
  • Qwen2.5-7B-Instruct法律应用:合同审查要点+修改建议+法条引用
  • IndexTTS-2-LLM真实项目案例:电子书语音转换系统教程
  • Qwen3-Reranker-0.6B应用解析:如何用rerank结果指导LLM生成更精准答案
  • SSTI 刷题记录
  • LiuJuan Z-ImageGPU算力方案:单卡4090支撑多任务并发生成实测
  • 浦语灵笔2.5-7B金融场景:K线图+新闻截图→行情解读→投资建议初稿
  • lite-avatar形象库惊艳案例:客服数字人7×24小时处理300+并发咨询无卡顿
  • Qwen2-VL-2B-Instruct实操手册:Streamlit界面调试信息与Device维度解析
  • [特殊字符] VSCode Copilot 里的大模型,到底是不是“真的”?一篇讲透它背后的控制权
  • DeOldify上色服务灾备方案:模型文件异地备份+服务配置Git版本管理
  • 实时口罩检测-通用模型标注规范说明:COCO格式转换实操
  • YOLO X Layout实战教程:结合PaddleOCR构建端到端文档理解Pipeline
  • AIGlasses_for_navigation代码实例:curl调用/api/config接口完成API Key动态更新
  • RabbitMQ交换机类型全解析:direct/fanout/topic/headers应用场景与代码实现
  • RMBG-2.0镜像免配置优势:预装PyTorch+OpenCV+Gradio,开箱即用不踩坑
  • Gemma-3-12b-it高性能推理部署:12B模型在RTX 4090×2环境下的实测表现
  • 2026年上海食品加工生产线哪家好?番茄酱、芒果浆、苹果汁、蘑菇酱、芒果汁、菠萝汁、枸杞、沙棘生产线厂家选择指南,加派机械深耕五十载的区域产业定制化伙伴 - 海棠依旧大
  • Chord视频理解工具实战案例:广告视频产品露出时段与位置热力图
  • 2026年荆州沙市区罗湖牌丸子:五家百年老店口碑与选购全指南 - 2026年企业推荐榜