练12:双指针

欢迎大家订阅【蓝桥杯Python每日一练】 专栏,开启你的 Python数据结构与算法 学习之旅!

文章目录

  • 前言
  • 1 同向扫描
  • 2 反向扫描
  • 3 同向扫描与反向扫描的对比
  • 4 例题分析
    • 2.1 回文判定
    • 2.2 美丽的区间
    • 2.3 挑选子串


前言

双指针是一种常用于数组和链表类问题中,指的是用两个指针在问题的输入数据结构中进行遍历。根据应用的场景和指针的运动方向,双指针可以分为同向扫描反向扫描两种类型。
在这里插入图片描述

1 同向扫描

同向扫描指的是两个指针从同一位置或相近的位置开始,向相同的方向移动。常见的应用场景包括:

  • 滑动窗口问题:通过一个窗口的左右边界来实现对区间的处理。
  • 寻找数组中满足某些条件的子数组
  • 合并两个已排序的数组(例如合并两个有序链表)。

优点

  • 使用双指针的技术可以在 O(n) 时间内完成遍历。
  • 避免了暴力搜索的 O(n²) 时间复杂度。

在这里插入图片描述


2 反向扫描

反向扫描指的是两个指针从不同的位置开始,分别向相反的方向移动。这种方法通常用于:

  • 排序问题:寻找两个数的和等于某个目标值,通常应用于已排序的数组。
  • 双端队列问题:需要同时从两端处理数据的场景。
  • 快排:快速排序算法通过反向扫描来处理左右子数组的元素。

优点

  • 反向扫描减少了重复计算,快速找到符合条件的解。
  • 适用于有序数据结构的情境,利用有序性减少搜索空间。

在这里插入图片描述


3 同向扫描与反向扫描的对比

特性同向扫描(Same Direction)反向扫描(Opposite Directions)
初始位置两个指针通常从相同的地方开始(例如同一个位置或相邻位置)两个指针通常从相反的地方开始(例如数组的两端)
指针移动方向指针沿着相同的方向逐步推进两个指针沿相反方向逐步推进
应用场景滑动窗口问题、子数组和问题、数组合并等排序数组中寻找目标和、快排、双端队列等
复杂度O(n),可以高效处理多个子数组和的情况O(n),适用于排序数组和快速排除不符合条件的解

4 例题分析

2.1 回文判定

在这里插入图片描述

题目地址:https://www.lanqiao.cn/problems/1371/learning/

样例输入

abcba

样例输出

Y

【示例代码】

s = input()
# 初始化左右指针 l 和 r,l 指向字符串的开始,r 指向字符串的结束
l, r = 0, len(s) - 1 
ok = 'Y' 
# 循环,直到左右指针交错(即 l > r)
while l <= r:  # 如果左右指针对应的字符相等if s[l] == s[r]:  l += 1  # 左指针向右移动r -= 1  # 右指针向左移动else:  # 如果ok = 'N'  break 
print(ok)  

【运行结果】
在这里插入图片描述

2.2 美丽的区间

在这里插入图片描述

题目地址:https://www.lanqiao.cn/problems/1372/learning/

样例输入

5 6
1 2 3 4 5

样例输出

2

【示例代码】

# 读取两个整数 n 和 S。n 是数组的长度,S 是最小和的阈值
n, S = map(int, input().split())  
# 读取一个数组 a,数组的长度是 n,包含 n 个整数
a = list(map(int, input().split()))  # 初始化左右指针,均指向数组的开始位置
left, right = 0, 0  
# 初始化 tot 变量为 0,用于记录当前区间 [left, right) 的和
tot = 0
# 初始化 min_len 为 n+1(一个大于数组长度的值),用于记录最小子数组长度  
min_len = n + 1  # 进入循环:通过左指针遍历每个子数组的起始位置
while left < n:# 不断拓展右端点,直到子数组的和大于或等于 Swhile right < n and tot < S:tot += a[right]  # 将右端点元素加入 totright += 1  # 右端点向右移动# 如果当前区间的和满足大于或等于 Sif tot >= S:# 更新最小长度,right - left 是当前区间的长度min_len = min(min_len, right - left)  # 左端点右移一位,缩小区间,继续寻找下一个合法区间# 将左端点元素从 tot 中移除tot -= a[left]  # 左端点右移left += 1  # 如果没有找到满足条件的子数组,min_len 会保持为 n+1
if min_len == n + 1:min_len = 0  # 如果没有找到合法区间,设置 min_len 为 0# 输出最小子数组长度
print(min_len)

【执行流程】
①初始化

  • n = 5S = 6
  • a = [1, 2, 3, 4, 5]
  • left = 0right = 0
  • tot = 0min_len = n + 1 = 6

②循坏处理

第一轮:left = 0

  • 进入 while left < n 循环。
  • 内层 while right < n and tot < S 开始:
    • right = 0tot = 0tot < S,因此进入循环:
      • tot += a[0] = 0 + 1 = 1right += 1right = 1
    • right = 1tot = 1tot < S,继续循环:
      • tot += a[1] = 1 + 2 = 3right += 1right = 2
    • right = 2tot = 3tot < S,继续循环:
      • tot += a[2] = 3 + 3 = 6right += 1right = 3
    • right = 3tot = 6,此时 tot >= S,跳出内层循环。
  • tot >= S 时,更新 min_len
    • min_len = min(min_len, right - left) = min(6, 3 - 0) = 3
  • 然后缩小区间,tot -= a[0] = 6 - 1 = 5left += 1left = 1

第二轮:left = 1

  • 进入 while left < n 循环。
  • 内层 while right < n and tot < S 继续执行:
    • right = 3tot = 5tot < S,继续循环:
      • tot += a[3] = 5 + 4 = 9right += 1right = 4
    • right = 4tot = 9,此时 tot >= S,跳出内层循环。
  • tot >= S 时,更新 min_len
    • min_len = min(min_len, right - left) = min(3, 4 - 1) = 3
  • 然后缩小区间,tot -= a[1] = 9 - 2 = 7left += 1left = 2

第三轮:left = 2

  • 进入 while left < n 循环。
  • 内层 while right < n and tot < S 继续执行:
    • right = 4tot = 7tot < S,继续循环:
      • tot += a[4] = 7 + 5 = 12right += 1right = 5
    • right = 5tot = 12,此时 tot >= S,跳出内层循环。
  • tot >= S 时,更新 min_len
    • min_len = min(min_len, right - left) = min(3, 5 - 2) = 3
  • 然后缩小区间,tot -= a[2] = 12 - 3 = 9left += 1left = 3

第四轮:left = 3

  • 进入 while left < n 循环。
  • 内层 while right < n and tot < S 不再执行,因为 right 已经达到数组的末尾。
  • tot >= S 时,更新 min_len
    • min_len = min(min_len, right - left) = min(3, 5 - 3) = 2
  • 然后缩小区间,tot -= a[3] = 9 - 4 = 5left += 1left = 4

第五轮:left = 4

  • 进入 while left < n 循环。
  • 内层 while right < n and tot < S 不再执行,因为 right 已经达到数组的末尾。
  • 此时,min_len = 2,退出外层循环。

③结果输出
最终 min_len = 2,即最小的子数组长度为 2。

【运行结果】
在这里插入图片描述

2.3 挑选子串

在这里插入图片描述

题目地址:https://www.lanqiao.cn/problems/1621/learning/

样例输入

7 4 2
4 2 7 7 6 5 1

样例输出

18

【示例代码】

# 读取三个整数 n (数组长度), m (阈值), k (至少有 k 个数 >= m)
n, m, k = map(int, input().split())  
# 读取数组 a,包含 n 个整数
a = list(map(int, input().split()))  
# 初始化左右指针,均指向数组的起始位置
left, right = 0, 0  ans = 0  # 用来记录满足条件的子数组数量
cnt = 0  # 记录当前窗口中大于等于 m 的元素个数# 主循环:左指针从0到n遍历数组
while left < n:# 内循环:扩展右指针,直到窗口中有至少 k 个元素大于等于 mwhile right < n and cnt < k:if a[right] >= m:  # 如果右指针指向的元素大于等于 mcnt += 1  # 计数窗口中大于等于 m 的元素数量right += 1  # 右指针向右移动,扩大窗口# 当窗口中有至少 k 个大于等于 m 的元素时if cnt >= k:# 计算从 left 到 right-1 的子数组的数量# 窗口为 [left, right-1],[left, right], [left, right+1], ..., [left, n-1],这些子数组都满足条件ans += n - right + 1  # right 已经移动到不满足条件的位置,所有从 left 到 [right, n-1] 的子数组都满足条件# 向右移动左指针,缩小窗口,准备进入下一轮if a[left] >= m:  # 如果左指针指向的元素大于等于 mcnt -= 1  # 移除左边界元素,更新窗口中大于等于 m 的元素数量left += 1  # 左指针右移# 输出满足条件的子数组数量
print(ans)

【执行流程】

①初始化
left = 0, right = 0, cnt = 0, ans = 0

②循坏处理
第一轮外循环 (left = 0)

  • 内循环拓展右指针,直到 cnt >= 2
    • right = 0: a[right] = 4 >= 4, cnt = 1
    • right = 1: a[right] = 2 < 4, cnt = 1
    • right = 2: a[right] = 7 >= 4, cnt = 2 → 窗口 [0, 2] 满足条件。
  • 当前 cnt = 2 >= 2,则有 n - right + 1 = 7 - 3 + 1 = 5 个符合条件的子数组,ans = 5
  • 左指针右移,left = 1cnt 减少 1

第二轮外循环 (left = 1)

  • 内循环继续拓展右指针,直到 cnt >= 2
    • right = 3: a[right] = 7 >= 4, cnt = 3 → 窗口 [1, 3] 满足条件。
  • 当前 cnt = 3 >= 2,则有 n - right + 1 = 7 - 4 + 1 = 4 个符合条件的子数组,ans = 5 + 4 = 9
  • 左指针右移,left = 2cnt 减少 1

第三轮外循环 (left = 2)

  • 内循环继续拓展右指针,直到 cnt >= 2
    • right = 4: a[right] = 6 >= 4, cnt = 3 → 窗口 [2, 4] 满足条件。
  • 当前 cnt = 3 >= 2,则有 n - right + 1 = 7 - 5 + 1 = 3 个符合条件的子数组,ans = 9 + 3 = 12
  • 左指针右移,left = 3cnt 减少 1

第四轮外循环 (left = 3)

  • 内循环继续拓展右指针,直到 cnt >= 2
    • right = 5: a[right] = 5 >= 4, cnt = 3 → 窗口 [3, 5] 满足条件。
  • 当前 cnt = 3 >= 2,则有 n - right + 1 = 7 - 6 + 1 = 2 个符合条件的子数组,ans = 12 + 2 = 14
  • 左指针右移,left = 4cnt 减少 1

第五轮外循环 (left = 4)

  • 内循环继续拓展右指针,直到 cnt >= 2
    • right = 6: a[right] = 1 < 4, cnt = 2 → 窗口 [4, 6] 满足条件。
  • 当前 cnt = 2 >= 2,则有 n - right + 1 = 7 - 7 + 1 = 1 个符合条件的子数组,ans = 14 + 1 = 15
  • 左指针右移,left = 5cnt 减少 1

第六轮外循环 (left = 5)

  • 右指针不再需要移动,cnt 少于 k,继续右移左指针,直到遍历结束。

③最终输出

18

【运行结果】
在这里插入图片描述

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

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

相关文章

360智脑张向征:共建可信可控AI生态 应对大模型安全挑战

发布 | 大力财经 人工智能的加速发展&#xff0c;有力推动了社会的数智化转型&#xff1b;与此同时&#xff0c;带来的相关安全风险也日益凸显。近日&#xff0c;在北京市举办的通明湖人工智能开发与应用大会上&#xff0c;360智脑总裁张向征以“大模型安全研究与实践”为主题&…

6.数据建模和数据检索及权限检查

总学习目录请点击下面连接 SAP ABAP开发从0到入职&#xff0c;冷冬备战-CSDN博客 目录 1.数据建模和ABAP字典的透明表 现实产品到数据库数据过程 飞行数据模型 做一个简单的引用。 从旅行社来看&#xff1a; 对于开发人员&#xff1a; 透明表 结构体和透明表 在系统中…

反复出现 idf.py: command not found 的解决办法

版本&#xff1a;ESP-IDF v4.4.8 1. 问题描述 当我们需要经常使用 ESP-IDF 时&#xff0c;总要反复安装编译链、设置环境&#xff0c;不然就会显示 idf.py: command not foundESP-IDF 是乐鑫官方的物联网开发框架&#xff0c;适用于ESP32、ESP32-S、ESP32-C 和ESP32-H 系列S…

IIC 通信协议

IIC 通信协议 参考&#xff1a;CSDN-Projectsauron、B站-江协科技 IIC Overview IIC协议&#xff08;Inter-Integrated Circuit&#xff0c;可简写为 I2C&#xff09;&#xff0c;是一种用于各种电子设备之间进行通信和数据交换的串行通信协议。它是由飞利浦&#xff08;Phil…

【图像处理】利用numpy、opencv、python实现车牌检测

| 利用opencv实现车牌检测 整体流程涉及5个部分 图像通道转换对比度增强边缘连接二值化边界区域裁剪 图像通道转换 将RGB图像转换为HSV图像&#xff0c;仅保留V通道。V通道表示颜色的明暗&#xff0c;常用于图像对比度拉伸、直方图均衡化等流程。 原图像&#xff1a; V通…

linux切换用户异常

1、报错现象 报错su: failed to execute /bin/bash: Resource temporarily unavailable 2、解决方案 vim /etc/security/limits.d/20-nproc.conf

UE5安装Fab插件

今天才知道原来Fab也有类似Quixel Bridge的插件&#xff0c;于是立马就安装上了&#xff0c;这里分享一下安装方法 在Epic客户端 - 库 - Fab Library 搜索 Fab 即可安装Fab插件 然后重启引擎&#xff0c;在插件面板勾选即可 然后在窗口这就有了 引擎左下角也会多出一个Fab图标…

Java、鸿蒙与嵌入式开发:技术选择与职业发展分析

在当今快速发展的科技领域中&#xff0c;Java、鸿蒙和嵌入式开发代表着不同的技术方向和职业机遇。每个方向都有其独特的市场价值和发展前景&#xff0c;让我们深入分析这三个领域的特点、发展趋势和职业规划。 Java开发方向已经发展了二十多年&#xff0c;仍然在软件开发领域…

synchronized 锁升级实现原理

synchronized 锁升级实现原理 对象的内存结构 在HotSpot虚拟机中&#xff0c;对象在内存中存储的布局可分为3块区域&#xff1a;对象头&#xff08;Header&#xff09;、实例数据&#xff08;Instance Data&#xff09;和对齐填充 我们需要重点分析MarkWord对象头 MarkWord …

vue3实现页签

效果 注意点 useStore涉及的部分是pina的缓存&#xff0c;需要改成自己的&#xff1b;userStore.tabStore是获取缓存里的页签&#xff0c;userStore.$patch(state > { state.tabStore tabStoreList.value }) 是存储改变的页签注意我的页签是根据路由path来判断的&#xf…

dfs算法搜索(详细)

目录 算法简介&#xff1a; 枚举方式&#xff1a; 1.每一个数都有两种状态&#xff0c;也就是选或不选&#xff0c;时间复杂度也就是2^n&#xff0c;每一个数都有选和不选两种状态。 2.生成给定集合所有可能排列的方法&#xff0c;与之不同的是同样是1 2 3三个数字&#xff0…

【机器学习】解构概率,重构世界:贝叶斯定理与智能世界的暗语

文章目录 条件概率与贝叶斯定理&#xff1a;深入理解机器学习中的概率关系前言一、条件概率与贝叶斯定理1.1 条件概率的定义与公式1.1.1 条件概率的定义1.1.2 条件概率的实例讲解 1.2 条件概率的性质与法则1.2.1 链式法则1.2.2 全概率公式1.2.3 贝叶斯定理的推导 1.3 贝叶斯定理…

利用开源Stable Diffusion模型实现图像压缩比竞争方法用更低的比特率生成更逼真的图像

概述 论文地址&#xff1a;https://studios.disneyresearch.com/app/uploads/2024/09/Lossy-Image-Compression-with-Foundation-Diffusion-Models-Paper.pdf 迪士尼的研究部门正在提供一种新的图像压缩方法&#xff0c;利用开源Stable Diffusion V1.2 模型&#xff0c;以比竞…

【Flask+OpenAI】利用Flask+OpenAI Key实现GPT4-智能AI对话接口demo - 从0到1手把手全教程(附源码)

文章目录 前言环境准备安装必要的库 生成OpenAI API代码实现详解导入必要的模块创建Flask应用实例配置OpenAI API完整代码如下&#xff08;demo源码&#xff09;代码解析 利用Postman调用接口 了解更多AI内容结尾 前言 Flask作为一个轻量级的Python Web框架&#xff0c;凭借其…

SpringBoot【十三(实战篇)】集成在线接口文档Swagger2

一、前言&#x1f525; 环境说明&#xff1a;Windows10 Idea2021.3.2 Jdk1.8 SpringBoot 2.3.1.RELEASE 二、如何生成Swagger文档 上一期我们已经能正常访问swagger在线文档&#xff0c;但是文档空空如也&#xff0c;对不对&#xff0c;接下来我就教大家怎么把相关的接口都给…

Qt之自定义动态调控是否显示日志

创作灵感 最近在芯驰x9hp上开发仪表应用。由于需要仪表警告音&#xff0c;所以在该平台上折腾并且调试仪表声音的时候&#xff0c;无意间发现使用&#xff1a; export QT_DEBUG_PLUGINS1 可以打印更详细的调试信息。于是想着自己开发的应用也可以这样搞&#xff0c;这样更方便…

Linux网络 UDP socket

背景知识 我们知道&#xff0c; IP 地址用来标识互联网中唯一的一台主机&#xff0c; port 用来标识该主机上唯一的一个网络进程&#xff0c;IPPort 就能表示互联网中唯一的一个进程。所以通信的时候&#xff0c;本质是两个互联网进程代表人来进行通信&#xff0c;{srcIp&…

数据链路层(Java)(MAC与IP的区别)

以太网协议&#xff1a; "以太⽹" 不是⼀种具体的⽹络, ⽽是⼀种技术标准; 既包含了数据链路层的内容, 也包含了⼀些物理 层的内容. 例如: 规定了⽹络拓扑结构, 访问控制⽅式, 传输速率等; 例如以太⽹中的⽹线必须使⽤双绞线; 传输速率有10M, 100M, 1000M等; 以太…

Apache APISIX快速入门

本文将介绍Apache APISIX&#xff0c;这是一个开源API网关&#xff0c;可以处理速率限制选项&#xff0c;并且可以轻松地完全控制外部流量对内部后端API服务的访问。我们将看看是什么使它从其他网关服务中脱颖而出。我们还将详细讨论如何开始使用Apache APISIX网关。 在深入讨…

项目15:简易扫雷--- 《跟着小王学Python·新手》

项目15&#xff1a;简易扫雷 — 《跟着小王学Python新手》 《跟着小王学Python》 是一套精心设计的Python学习教程&#xff0c;适合各个层次的学习者。本教程从基础语法入手&#xff0c;逐步深入到高级应用&#xff0c;以实例驱动的方式&#xff0c;帮助学习者逐步掌握Python的…