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

基础算法--双指针【概念+图解+题解+解释】

 更多精彩内容.....

🎉❤️播主の主页✨😘

Stark、-CSDN博客

本文所在专栏:

数据结构与算法_Stark、的博客-CSDN博客

其它专栏:

学习专栏C语言_Stark、的博客-CSDN博客

项目实战C系列_Stark、的博客-CSDN博客​​​​​​

座右铭:梦想是一盏明灯,照亮我们前行的路,无论风雨多大,我们都要坚持不懈。


双指针概念

双指针技巧是 C++ 编程中的一个常用且强大的方法,特别是在处理数组或链表问题时。这种技巧通常有两种主要形态:滑动窗口和快慢指针。接下来,我将详细讲解这两种方法,包括基本原理、使用场景以及代码示例。

一、滑动窗口(Sliding Window)

滑动窗口可以用来解决一系列数组或字符串问题,尤其是当需要处理“连续”子数组/子字符串时特别有用。其基本思想是使用两个指针(或索引)来表示一个窗口的起始和结束位置,通过移动这些指针来逐步扩展或收缩窗口。

1. 基本思路
  • 使用两个指针(left 和 right),left 表示窗口的开始位置,right 表示窗口的结束位置。
  • 增加 right 拓展窗口,直到满足某个条件。
  • 一旦满足条件,就尝试移动 left 来收缩窗口,直到条件不再满足。
2. 使用场景
  • 查找最长或最短的连续子数组/子字符串。
  • 字符串的无重复字符子串问题。
3. 代码示例

以下是寻找给定字符串中,最长无重复字符子串的代码示例:

#include <iostream>  
#include <unordered_set>  
#include <string>  
using namespace std;int lengthOfLongestSubstring(std::string s) {  unordered_set<char> charSet;  int left = 0, maxLength = 0;  for (int right = 0; right < s.length(); right++) {  while (charSet.find(s[right]) != charSet.end()) {  charSet.erase(s[left]);  left++;  }  charSet.insert(s[right]);  maxLength = max(maxLength, right - left + 1);  }  return maxLength;  
}  int main() {  string s = "abcabcbb";  cout << "Longest substring without repeating characters: " ;cout << lengthOfLongestSubstring(s) << std::endl;  return 0;  
}  

二、快慢指针(Fast and Slow Pointers)

快慢指针技巧通常用于链表和数组中,其基本概念是使用两个指针以不同的速度遍历结构。

1. 基本思路
  • 一个指针(慢指针)每次向前移动一步,另一个指针(快指针)每次向前移动两步。
  • 这种方式使得快指针走得比慢指针快,从而可以检测到特定条件(如环的存在)。
2. 使用场景
  • 检测链表是否有环。
  • 寻找链表的中间节点。
3. 代码示例

以下是检测链表是否有环的代码示例:

#include <iostream>  
using namespacestruct ListNode {  int val;  ListNode *next;  ListNode(int x) : val(x), next(NULL) {}  
};  bool hasCycle(ListNode *head) {  if (!head) return false;  ListNode *slow = head;  ListNode *fast = head;  while (fast && fast->next) {  slow = slow->next;         // 慢指针走一步  fast = fast->next->next;   // 快指针走两步  if (slow == fast) {  return true;           // 如果相遇,说明有环  }  }  return false;                  // 遍历完没有相遇,说明没有环  
}  int main() {  ListNode *head = new ListNode(3);  head->next = new ListNode(2);  head->next->next = new ListNode(0);  head->next->next->next = new ListNode(-4);  head->next->next->next->next = head->next; // 创建环  if (hasCycle(head)) {  cout << "List has a cycle." << endl;  } else {  cout << "List does not have a cycle." << endl;  }  return 0;  
}  

总结/其他场景

双指针技术是一种高效且灵活的算法策略,对于多种问题都可以应用。在使用双指针时,理解问题的结构及条件是至关重要的。熟练掌握滑动窗口和快慢指针后,可以解决很多典型算法问题。

运用双双指针的其它场景:

  1. 有序数组的两数之和:在有序数组中找到两个数,使它们的和等于目标值。
  2. 反转字符串:使用双指针可以有效地反转一个字符串。
  3. 寻找回文串:通过两个指针从两端向中间移动,判断字符串是否回文。
  4. 合并两个有序数组:使用两个指针分别指向两个数组的起始位置,进行合并。

算法真题实训

开胃菜:移动零

283. 移动零 - 力扣(LeetCode)

题目:给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。请注意 ,必须在不复制数组的情况下原地对数组进行操作。

在这道题中,我们需要将所有的零全部移动到数组末尾不能改变原来非零元素的相对位置。一开始我们的思路可以暴力一些,直接就是两层循环,实现复杂度为O(n²)的代码。不过考虑到数据范围:

  • 1 <= nums.length <= 104
  • -2^31 <= nums[i] <= 2^31 - 1

这样做就很容易就超时了,不太保险。那么我们必须想办法优化一下。那么我们就可以使用双指针的思想,定义两个变量记录位置。一个表示零定位指针pre,一个表示非零定位指针cur。

通过循环将零定位指针pre移动到第一个零元素位置,非零定位指针cur移动到pre后面的第一个非零元素位置。交换两个指针的值。

class Solution {
public:void moveZeroes(vector<int>& v) {int n = v.size();int pre = 0, cur = 0;//两个指针同时出发while (cur < n) {swap(v[pre], v[cur]);while (pre < n && v[pre])pre++; // 如果前面不为0,前面往后走。while (cur < n && !v[cur])cur++; // 如果后面为0,后面往后走。if (pre > cur)swap(pre, cur);}}
};class Solution {
public:void moveZeroes(vector<int>& v) {int n=v.size();int pre = 0, cur = 1;//两个指针一前一后while (cur<n) {if (!v[pre] && v[cur]) swap(v[pre], v[cur]);//如果前面为0,后面不为零,交换while (pre<n&&v[pre])pre++;//如果前面不为0,前面往后走。while (cur<n&&!v[cur])cur++;//如果后面为0,后面往后走。if (pre > cur)swap(pre, cur);}}
};

进阶篇:有效三角形的个数 

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

题目:给定一个包含非负整数的数组 nums ,返回其中可以组成三角形三条边的三元组个数。

同样的,我们可以使用三个指针来循环遍历所有的三元组看是否能构成三角形。进行简单的修改后我们才能通过,只是复杂度会很高。

class Solution {
public:int triangleNumber(vector<int>& v) {sort(v.begin(), v.end());//先进行排序int sum = 0;int fst = 0;for (auto e : v) {if (e == 0)fst++;else break;}//跳过所有的0元素for (int i = fst; i < v.size()-2; i++) {//第一条边,从第一个非零元素开始到最后。for (int j = i + 1; j < v.size()-1; j++) {//第二条边,从第一条边的下一个元素开始int max = v[j] + v[i];//两边之和int min = v[j] - v[i];//两边之差int p = 0, q = 0;for (int k = j + 1; k < v.size(); k++) {//第三条边从第二条边下一个元素开始if (!p && v[k] > min)p = k;//如果q还没找到合适的位置,那么这时候只要第三边大于两边之差即可确定范围上限if (!q && v[k] >= max)q = k;//如果p还没找到合适的位置,那么这时候只要第三边大于两边之和,就不再能确定三角形了if (p && q) break;//如果两个范围都找到了,提前结束循环。}if (p&&!q)q = v.size();//如果正常结束循环,没有进行break,q就没有被赋值,那么q=v.size();sum += (q - p);//q、p两个下标位置之间的都可以作为第一、二边的第三边。计入总数。}}return sum;//返回总数。}
};

能跑过,但这需要你考虑很多小的细节,还有那么一点可能跑不过测试。我们在算法比赛中不能冒险,所以我们需要其它的方法确保万无一失。什么方法呢?双指针! 双指针是两个指针,我们要找三个元素的关系,怎么办呢?我们可以选择将一个边用来循环,另外两条边进行双指针化解答。

class Solution {
public:int triangleNumber(vector<int>& nums) {int n=v.size(); sort(nums.begin(), nums.end());int sum = 0;for (int i = n - 1; i >= 2; i--) {int pre = 0, cur = i - 1;while (pre < cur) {if (nums[pre] + nums[cur] > nums[i]) {sum += (cur - pre);cur--;} else pre++;}}return sum;}
};

感谢大家观看,持续关注博主,了解更多算法。 

相关文章:

基础算法--双指针【概念+图解+题解+解释】

更多精彩内容..... &#x1f389;❤️播主の主页✨&#x1f618; Stark、-CSDN博客 本文所在专栏&#xff1a; 数据结构与算法_Stark、的博客-CSDN博客 其它专栏&#xff1a; 学习专栏C语言_Stark、的博客-CSDN博客 项目实战C系列_Stark、的博客-CSDN博客​​​​​​ 座右铭&a…...

国产化系统/鸿蒙开发足浴店收银源码-收缩左侧———未来之窗行业应用跨平台架构

一、左侧展开后 二、代码 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <html> <head><title></title><meta http-equiv"Content-Type" content"text/html; charsetUTF-8"><style t…...

如何从硬盘恢复丢失/删除的视频

您是否想知道是否可以恢复已删除的视频&#xff1f; 幸运的是&#xff0c;您可以使用奇客数据恢复从硬盘驱动器、SD 卡和 USB 闪存驱动器恢复已删除的视频文件。 你有没有遇到过这样的情况&#xff1a;当你随机删除文件以释放空间时&#xff0c;你不小心按下了一些重要视频的…...

《Effective C++》第三版——设计与声明(1)

参考资料&#xff1a; 《Effective C》第三版 注意&#xff1a;《Effective C》不涉及任何 C11 的内容&#xff0c;因此其中的部分准则可能在 C11 出现后有更好的实现方式。 条款 18&#xff1a;让接口容易被正确使用&#xff0c;不易被误用 好的接口很容易被正确使用&…...

数值计算的程序设计问题举例

### 数值计算的程序设计问题 #### 1. 结构静力分析计算 **涉及领域**&#xff1a;工程力学、建筑工程 **主要问题**&#xff1a;线性代数方程组&#xff08;Linear Algebraic Equations&#xff09; **解释说明**&#xff1a; 在结构静力分析中&#xff0c;我们需要解决复杂的…...

Java之方法的使用

修饰符 返回值 方法名称&#xff08;形式参数&#xff09;{ } 当无参数的时候形式参数中什么都不写。 列如求两个数相加 修饰符可有可无。 方法重载&#xff1a; 1.方法名相同 2.参数列表不同 3。返回值不影响重载...

sudo 命令:掌握系统权限控制,实现安全高效管理

一、命令简介 ​sudo​ 命令允许系统管理员授权普通用户执行特定命令&#xff0c;并以管理员身份运行这些命令&#xff0c;通常需要输入用户自己的密码。 ​​ sudo 全称是"substitute user do"&#xff0c;意为“替用户做”&#xff0c;也就是“以另一个用户的身…...

AndroidStudio导入so文件

点击app 右键依次选择New-Floder-JNI Floder 创建jni目录 将需要的so文件拷贝到jni目录 在app目录下&#xff0c;build.gradle文件的android{}中添加&#xff1a; sourceSets {main{jniLibs.srcDirs [src/main/jni]}}点击一下Sync Project with Gradle Files 然后编译生成AP…...

Kuebernetes 群集基于 Docker 部署

Kuebernetes 群集基于 Docker 部署 实验报告资源列表基础环境一、准备 Docker1、安装 Docker 二、安装 Kubeadm 工具1、配置 yum 源2、安装 Kubeadm 工具 三、初始化 Master 节点1、配置 Master 节点2、常见故障 四、Node 节点加入集群五、部署网络插件&#xff08;CNI&#xf…...

追随 HarmonyOS NEXT,Solon v3.0 将在10月8日发布

Solon &#xff08;开放原子开源基金会&#xff0c;孵化项目&#xff09;原计划10月1日发布 v3.0 正式版。看到 HarmonyOS NEXT 将在 10月8日启用公测&#xff0c;现改为10月8日发布以示庆贺。另外&#xff0c;Solon 将在2025年启动“仓颉”版开发&#xff08;届时&#xff0c;…...

服装时尚与动漫游戏的跨界联动:创新运营与策划策略研究

摘要&#xff1a;本论文聚焦于服装时尚与动漫游戏的跨界联动现象&#xff0c;深入探讨其在运营和策划方向的策略与实践。通过对相关理论的梳理和实际案例的分析&#xff0c;阐述了跨界联动的背景、意义、模式以及面临的挑战。研究发现&#xff0c;成功的跨界联动能够实现品牌价…...

Redis中String类型的常用命令(append,getrenge,setrange等命令)

Redis----String命令 前言.常见的String存储类型. 常见命令1. set 命令2. get 命令3. mget命令与mset命令4. setnx命令5. setex与psetex命令6. incr与incrby与incrbyfloat命令7. decr与decrby命令8. append命令9. getrange和setrange命令10. strlen命令. 前言. 常见的String存…...

深度拆解:如何在Facebook上做跨境电商?

国内社交媒体正在逐渐兴盛&#xff0c;海外也不例外。在数字营销的新时代&#xff0c;Facebook已成为跨境电商不可或缺的平台之一。通过Facebook的巨大流量&#xff0c;卖家可以更好的触及潜在消费者&#xff0c;以实现销售增长。本文就深度拆解一下&#xff0c;卖家如何利用Fb…...

为啥数据需转换成tensor才能参与后续建模训练

将数据转换为Tensor&#xff08;张量&#xff09;格式用于深度学习和机器学习模型训练&#xff0c;主要是出于以下几个关键原因&#xff1a; 数值计算的效率&#xff1a;Tensor&#xff08;由PyTorch、TensorFlow等库提供&#xff09;是在GPU上执行高效的数值运算的数据结构。相…...

leetcode:380. O(1) 时间插入、删除和获取随机元素

实现RandomizedSet 类&#xff1a; RandomizedSet() 初始化 RandomizedSet 对象bool insert(int val) 当元素 val 不存在时&#xff0c;向集合中插入该项&#xff0c;并返回 true &#xff1b;否则&#xff0c;返回 false 。bool remove(int val) 当元素 val 存在时&#xff0…...

Linux集群部署RabbitMQ

目录 一、准备三台虚拟机&#xff0c;配置相同 1、所有主机都需要hosts文件解析 2、所有主机安装erLang和rabbitmq 3、修改配置文件 4、导入rabbitmq 的管理界面 5、查看节点状态 6、设置erlang运行节点 7、rabitmq2和rabbitmq3重启服务 8、查看各个节点状态 二、添加…...

01DSP学习-了解DSP外设-以逆变器控制为例

(由于是回忆自己简单的DSP学习过程&#xff0c;所以博客看起来有些没有章法&#xff0c;请见谅~) 上一篇博客介绍了学习DSP需要的软件和硬件准备&#xff0c;以及一个DSP的工程包含了哪些东西。我的学习方法是目的导向&#xff0c;即我需要用什么我就学什么&#xff0c;并没有…...

【ArcGIS Pro实操第三期】多模式道路网构建(Multi-model road network construction)原理及实操案例

ArcGIS Pro实操第三期&#xff1a;多模式道路网构建原理及实操案例 1 概述1.1 原理 2 GIS实操2.1 新建文件并导入数据2.2 创建网络数据集2.3 设置连接策略&#xff08;Setting up connectivity policies&#xff09;2.4 添加成本&#xff08;Adding cost attributes&#xff09…...

深度学习基础及技巧

机器学习中的监督学习 监督学习是通过对数据进行分析&#xff0c;找到数据的表达模型&#xff0c;对新输入的数据套用该模型做决策 主要分为训练和预测两个阶段 训练阶段&#xff1a;根据原始数据进行特征提取&#xff0c;然后使用决策树、随机森林等模型算法分析数据之间的特…...

Unity 外描边简单实现(Shader Graph)

1&#xff1a;原理 将物体的模型空间的位置&#xff08;也就是顶点数据&#xff09;放大&#xff0c;作为一个单独的渲染通道单独渲染&#xff0c;这时候模型是已经发大过的&#xff0c;要想看到外描边的效果&#xff0c;需要将正面显示的东西给去掉&#xff0c;显示背面渲染的…...

Java 语言特性(面试系列2)

一、SQL 基础 1. 复杂查询 &#xff08;1&#xff09;连接查询&#xff08;JOIN&#xff09; 内连接&#xff08;INNER JOIN&#xff09;&#xff1a;返回两表匹配的记录。 SELECT e.name, d.dept_name FROM employees e INNER JOIN departments d ON e.dept_id d.dept_id; 左…...

应用升级/灾备测试时使用guarantee 闪回点迅速回退

1.场景 应用要升级,当升级失败时,数据库回退到升级前. 要测试系统,测试完成后,数据库要回退到测试前。 相对于RMAN恢复需要很长时间&#xff0c; 数据库闪回只需要几分钟。 2.技术实现 数据库设置 2个db_recovery参数 创建guarantee闪回点&#xff0c;不需要开启数据库闪回。…...

突破不可导策略的训练难题:零阶优化与强化学习的深度嵌合

强化学习&#xff08;Reinforcement Learning, RL&#xff09;是工业领域智能控制的重要方法。它的基本原理是将最优控制问题建模为马尔可夫决策过程&#xff0c;然后使用强化学习的Actor-Critic机制&#xff08;中文译作“知行互动”机制&#xff09;&#xff0c;逐步迭代求解…...

渗透实战PortSwigger靶场-XSS Lab 14:大多数标签和属性被阻止

<script>标签被拦截 我们需要把全部可用的 tag 和 event 进行暴力破解 XSS cheat sheet&#xff1a; https://portswigger.net/web-security/cross-site-scripting/cheat-sheet 通过爆破发现body可以用 再把全部 events 放进去爆破 这些 event 全部可用 <body onres…...

如何在看板中有效管理突发紧急任务

在看板中有效管理突发紧急任务需要&#xff1a;设立专门的紧急任务通道、重新调整任务优先级、保持适度的WIP&#xff08;Work-in-Progress&#xff09;弹性、优化任务处理流程、提高团队应对突发情况的敏捷性。其中&#xff0c;设立专门的紧急任务通道尤为重要&#xff0c;这能…...

【服务器压力测试】本地PC电脑作为服务器运行时出现卡顿和资源紧张(Windows/Linux)

要让本地PC电脑作为服务器运行时出现卡顿和资源紧张的情况&#xff0c;可以通过以下几种方式模拟或触发&#xff1a; 1. 增加CPU负载 运行大量计算密集型任务&#xff0c;例如&#xff1a; 使用多线程循环执行复杂计算&#xff08;如数学运算、加密解密等&#xff09;。运行图…...

C# SqlSugar:依赖注入与仓储模式实践

C# SqlSugar&#xff1a;依赖注入与仓储模式实践 在 C# 的应用开发中&#xff0c;数据库操作是必不可少的环节。为了让数据访问层更加简洁、高效且易于维护&#xff0c;许多开发者会选择成熟的 ORM&#xff08;对象关系映射&#xff09;框架&#xff0c;SqlSugar 就是其中备受…...

Android15默认授权浮窗权限

我们经常有那种需求&#xff0c;客户需要定制的apk集成在ROM中&#xff0c;并且默认授予其【显示在其他应用的上层】权限&#xff0c;也就是我们常说的浮窗权限&#xff0c;那么我们就可以通过以下方法在wms、ams等系统服务的systemReady()方法中调用即可实现预置应用默认授权浮…...

Java毕业设计:WML信息查询与后端信息发布系统开发

JAVAWML信息查询与后端信息发布系统实现 一、系统概述 本系统基于Java和WML(无线标记语言)技术开发&#xff0c;实现了移动设备上的信息查询与后端信息发布功能。系统采用B/S架构&#xff0c;服务器端使用Java Servlet处理请求&#xff0c;数据库采用MySQL存储信息&#xff0…...

代码规范和架构【立芯理论一】(2025.06.08)

1、代码规范的目标 代码简洁精炼、美观&#xff0c;可持续性好高效率高复用&#xff0c;可移植性好高内聚&#xff0c;低耦合没有冗余规范性&#xff0c;代码有规可循&#xff0c;可以看出自己当时的思考过程特殊排版&#xff0c;特殊语法&#xff0c;特殊指令&#xff0c;必须…...