关于“双指针法“的总结

笔者这些天终于达成了只狼的全成就,甚是欢喜。然而乐极生悲,最近做了些算法题,竟没有一道靠自己做出来。

感觉算法题常常用到“双指针法”呢……为什么到现在我还是做不出来这些算法题……

今天就来试着总结一下它的使用场景吧。


快慢指针法

又名为“同向指针”,常见于“原地修改数组”的情况。LeetCode.283的移动零就需要通过快慢指针来实现。

题目要求将数组中所有的0移动到末尾。但是我们习惯于数组中数据前移的操作,所以我们将问题转换为“将非零数据前移并在末尾填充0”。

而快慢指针天然适用于这种场景:快指针跳过无用数据快速遍历,慢指针从头到尾依次递增方便数据覆盖。简单来说就是快指针的元素覆盖慢指针的元素。

这样一来,前移时,一个指针存放需要前移的元素(快指针),一个指针存放需要被覆盖的位置(慢指针)。最后快指针到达终点时,慢指针未遍历的元素数量就是快指针跳过的元素数量。

这里以LeetCode.80删除有序数组中的重复项为例。

不同于LeetCode.26的元素只出现一次,这里需要能够存放两个元素。那么对于这种原地修改数组的问题,我们依旧采用前指针覆盖后指针的策略。对于第26题,后指针作为被覆盖的元素,其移动受制于前指针是否发现新的不重复元素。而80题的后指针移动,不光受制于元素的不重复,也受制于后指针的前面是否够两个重复元素,如果有,就覆盖(始终从后指针的角度考虑,那些元素该保留,那些元素该被覆盖)。

class Solution {
public:int removeDuplicates(vector<int>& nums) {int x = 0;int y = 0;while(y < nums.size()){if(y <= 1){++x;}//因为元素有序,通过x-2判断同时保证了元素成功前移else if(y > 1 && nums[x-2] != nums[y]){nums[x] = nums[y];++x;}++y;}return x;}
};

相向指针法

两指针对向行驶,常见于“反转元素”的情况。LeetCode.345的反转元音字母。如果用while迭代的话,在处理完后记得让指针继续行驶。

相向指针法也常常会与“贪心算法”的思想结合。常常作为条件来判断指针移动的时机。经典的是“盛水体积”问题,每次选择最短的木板移动。因为我们确定,局部的选择短板移动最终促成整体能够找到体积最大的状态(移动长板必定变小)。

这里以LeetCode.680验证回文串为例,题目要求最多删除一个字符,其能够成为回文串。这种对称的结构天然适合对向指针,只要两边一样就让两个指针行驶。

但如果两边字符不一样呢?最多删除一次的限制,我们应该考虑怎样的局部最优解能最终得出回文串。此时,若删除字符后剩余的字符串是回文串即可,那最终就是回文串。

class Solution {
public:bool validPalindrome(string s) {int left = 0;int right = s.size() - 1;bool canChange = true;while(left < right){if(s[left] == s[right]){++left;--right;}else{//两端不一样时,需要查看两种删除的情况return check(s, left + 1, right) || check(s, left, right - 1);}}//到最后就是普通回文串的情况return true;}//新建函数判断普通的回文串bool check(string s, int left, int right){for(int i = left, j = right; i < j; ++i, --j){if(s[i] != s[j]){return false;}}return true;}
};

​​​​​​​但还有一种情况是需要我们先转化之后再使用对向指针的类型。比如经典的三数之和。

如果是两数之和的话,可以直接使用双指针解开。按照这个思路,三数之和的话,固定一个数,那么剩余就是两数之和等于这个数的负数的问题。就这样继续用双指针。

class Solution {
public:vector<vector<int>> threeSum(vector<int>& nums) {vector<vector<int>> vec;sort(nums.begin(), nums.end());for(int i = 0; i < nums.size() - 2 && nums[i] <= 0; i++){if(i > 0 && nums[i] == nums[i - 1]){continue;}int left = i + 1;int right = nums.size() - 1;int target = -nums[i];while(left < right){//去重if(left > i + 1 && nums[left] == nums[left - 1]){++left;continue;}int sum = nums[left] + nums[right];if(sum == target){vec.push_back({nums[i], nums[left], nums[right]});++left;--right;}else if(sum < target){++left;}else{--right;}}}return vec;}
};

​​​​​​​还有LeetCode.42的接雨水问题,这类问题的输出总是与两端元素的变化有关系,就需要考虑使用相向指针法解决。

滑动窗口

滑动窗口是一种较为特殊的同向指针,它通过两个指针来维护一个窗口,这个“窗口”通常是具有某种性质的连续元素或子串。

比较经典的就是无重复字符串的最长子串。前后指针从起点出发,前指针用于扩展窗口,并每次检测窗口“无重复字符”的性质是否改变。后指针用于收缩窗口,当字符开始出现重复,则进行收缩,当性质满足时,继续前指针扩展。这样一来,便可以遍历所有无重复字符的子串。

class Solution {
public:int lengthOfLongestSubstring(string s) {int left = 0;int right = 0;int length = 0;int maxLength = 0;vector<char> chars;bool skip = false;while(right < s.size()){char cur = s[right];for(auto c : chars){if(c == cur){//删除第一个元素chars.erase(chars.begin());++left;length = right - left;skip = true;//这里的continue对for起作用,不对while起作用break;}}if(skip){skip = false;continue;}chars.push_back(cur);++right;length = right - left;if(length > maxLength){maxLength = length;}}return maxLength;}};

​​​​​​​需要注意的是,for中的continue不对外部的while起作用。

现在以这道简单题为例,写一下滑动窗口的解法。

class Solution {
public:double findMaxAverage(vector<int>& nums, int k) {int left = 0;int right = k - 1;int sum = 0;for(int i = 0; i < k; i++){sum += nums[i];}int maxSum = sum;while(right + 1 < nums.size()){int newSum = sum - nums[left] + nums[right + 1];if(newSum > maxSum){maxSum = newSum;}            sum = newSum;++left;++right;}return (double)maxSum/k;}
};

小结

这便是双指针的3种常见使用方式。

如有补充交流欢迎留言,我们下次再见~

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

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

相关文章

基于51单片机的智能吊灯

基于 51 单片机的智能吊灯设计与实现论文简纲一、引言1.1 研究背景与意义阐述传统照明设备在节能性、智能化方面的不足&#xff0c;结合智能家居产业发展趋势&#xff0c;说明设计基于 51 单片机的智能吊灯对提升生活便利性、降低能耗的现实意义。1.2 国内外研究现状简要介绍当…

CF每日三题(1500-1700)

1792C 逆向思维1036D 前缀和尺取1598D 组合数学取三元组 将二元组放在坐标系中更好找到规律 1792C 思维 1500 参考题解 正难则反 注意是对一个排列进行操作&#xff0c;最后还原成1,2,…,n 每次选两个数字很难想&#xff0c;反着想就是把1-n的排列变成所给数组的逆操作&#x…

Boost搜索引擎项目(详细思路版)

目录 项目相关背景 搜索引擎原理技术栈和项目环境 导入数据到自己的本地 数据去标签与数据清洗模块 Enumfile(src_path, &file_list)递归式写入 Parsehtml(file_list, &results)去标签 bool Parsetitle(const string& file, string* title)拆分标题 bool Pa…

AI产品经理面试宝典第69天:大模型稳定性评估与AI伦理挑战面试题全解析

1. AI伦理与技术挑战 1.1 问:你认为AI的最大挑战是什么? 答:AI面临的最大挑战是算法偏见与模型黑箱问题。具体表现为: 数据偏见放大:训练数据中隐含的性别、种族等偏见会被模型继承,如招聘算法中的性别歧视案例 决策透明性缺失:深度学习模型的可解释性不足,医疗诊断场…

【build】RDK构建系统v0.1 (持续更新。。。。)

一、 项目概述RDK构建系统是一个用于构建和定制嵌入式系统的自动化工具&#xff0c;通过简单的命令行操作&#xff0c;您可以完成从下载依赖包、定制根文件系统、构建内核到打包镜像的完整流程。该系统采用模块化设计&#xff0c;提供了丰富的配置选项&#xff0c;适用于不同的…

关于RSA和AES加密

RSA非对称加密 非对称加密不能传输大数据量&#xff0c;但比对称加密要安全&#xff0c;所以传输密码一般就是用的非对称加密 接口拿到RSA公钥然后再加密之后传给后端就好了 let crypt new JSEncrypt(); crypt.setPublicKey(res.message); // console.log(加密前:, data); let…

云蝠智能VoiceAgent:AI赋能售后服务场景的创新实践

引言&#xff1a;售后服务数字化转型的必然趋势在数字经济时代&#xff0c;售后服务已成为企业核心竞争力的重要组成部分。据统计&#xff0c;优质的售后服务能够提升客户留存率高达67%&#xff0c;同时降低客户获取成本约30%。然而&#xff0c;传统售后服务模式面临着人力成本…

C#控制台输入(Read()、ReadKey()和ReadLine())

下面我们来详细讲解 C# 中三种控制台输入方法&#xff1a;Console.Read()、Console.ReadKey() 和 Console.ReadLine() 的区别、原理、使用场景&#xff0c;并配上清晰的代码例子和运行结果说明。✅ 一、三者的根本区别&#xff08;一句话总结&#xff09;方法返回值读取方式是否…

Windows的Roaming文件夹的作用和Local/LocalLow的区别

&#x1f4c1; Roaming 文件夹的核心意义✅ 什么是“漫游”&#xff08;Roaming&#xff09;&#xff1f;跨设备同步&#xff1a;当用户登录到同一域内的不同 Windows 设备&#xff08;如公司或学校的办公电脑&#xff09;时&#xff0c;Roaming 文件夹中的数据会自动通过网络同…

【Java Web 快速入门】十一、Spring Boot 原理

目录Spring Boot 原理配置优先级Bean 管理获取 BeanBean 的作用域第三方 BeanSpring Boot 底层原理起步依赖自动配置核心原理实例说明例 1&#xff1a;自定义一个 “日志 starter”例 2&#xff1a;SpringBoot 自带的 spring-boot-starter-web关键总结Spring Boot 原理 配置优…

基于Redisson的分布式锁原理深度解析与优化实践

基于Redisson的分布式锁原理深度解析与优化实践 分布式环境下&#xff0c;锁的实现至关重要。本文将从技术背景与应用场景出发&#xff0c;结合核心原理、关键源码、实际示例&#xff0c;深入剖析Redisson分布式锁的实现机制&#xff0c;并给出性能优化建议&#xff0c;帮助后端…

室外 3DVG 基准

室外 3DVG基准&#xff08;按重要性与被引用频率&#xff09; Talk2Car / Talk2Car-3D (2019 / 衍生) — 对象 referral&#xff08;驾驶场景&#xff09; 说明&#xff1a;最早的自然语言 → 驾驶场景对象引用数据集之一&#xff08;原 Talk2Car 是以 nuScenes 为底并提供自然…

Jenkins安装部署(Win11)和常见配置镜像加速

一、安装前准备 本文使用的Jenkins Windows一键安装包&#xff0c;JDK事先配置好环境变量&#xff0c;Jenkins版本&#xff1a; Jenkins下载地址&#xff1a;jenkins一键安装包v2-479-1.msi资源-CSDN下载 二、Jenkins安装部署 1、下载Jenkins &#xff0c;点击下一步下一步…

Windows MCP.Net:革命性的 .NET Windows 桌面自动化 MCP 服务器

&#x1f4cb; 目录 项目概述 核心技术架构 功能特性详解 技术实现亮点 安装与配置 实战应用场景 代码示例与API详解 性能优化与最佳实践 未来发展规划 总结 项目概述 在人工智能快速发展的今天&#xff0c;AI 助手与操作系统的深度集成成为了一个重要趋势。Window…

Java ArrayList的介绍及用法

十分想念顺店杂可。。。ArrayList 是 Java 集合框架中最常用的类之一&#xff0c;实现了 List 接口&#xff0c;底层基于动态数组实现&#xff0c;支持动态扩容&#xff0c;相比普通数组更灵活。以下是其详细介绍及用法&#xff1a;一、核心特性动态大小&#xff1a;无需预先指…

Docker 命令大全及使用场景总结

一、容器生命周期管理1. 创建并运行容器docker run [选项] 镜像名 [命令]常用选项&#xff1a;-d&#xff1a;后台运行&#xff08;detached&#xff09;-it&#xff1a;交互式终端&#xff08;如 -it ubuntu bash&#xff09;--name&#xff1a;指定容器名称-p 主机端口:容器端…

简单的 HTTPS 学习

简单的 HTTPS 学习 1. 需求 现在使用的服务是HTTP调用形式&#xff0c;服务可能会有调用外围https形式的服务&#xff0c;简单了解了一下&#xff0c;然后写了一个简单的例子进行记录。 HTTP&#xff08;超文本传输协议&#xff09; 是一种用于传输超文本的应用层协议&#…

[系统架构设计师]系统质量属性与架构评估(八)

[系统架构设计师]系统质量属性与架构评估&#xff08;八&#xff09; 一.软件系统质量属性 1.基本概念 软件系统质量属性&#xff1a;可测量或可测试的属性 开发期质量属性&#xff0c;运行期质量属性面向架构评估的质量属性&#xff1a;1.可用性&#xff1a; 提升策略 错误检测…

【R语言】R 语言中 gsub 与正则表达式详解(含 POSIX 与 Perl 风格实例)

R 语言中 gsub 与正则表达式详解&#xff08;含 POSIX 与 Perl 风格实例&#xff09; 在 R 语言中&#xff0c;字符串处理是非常常见的需求&#xff0c;R 语言中的 gsub() 函数则具有字符串替换的功能。本文将通过两个实例&#xff0c;帮助你深入理解 R 的 gsub()、POSIX 字符…

EN55035多媒体设备电磁兼容性抗干扰要求标准

EN55035 是一项由欧洲标准化委员会制定的电磁兼容性&#xff08;EMC&#xff09;标准&#xff0c;全称为《多媒体设备的电磁兼容性要求》。该标准主要针对多媒体设备的电磁辐射和抗干扰能力进行规范&#xff0c;确保这类设备在电磁环境中能够正常工作&#xff0c;同时不对其他设…