算法解题之双指针

简介

双指针技术是一种常见的算法设计思路,它通常适用于处理有序数组或链表等数据结构。双指针算法通过使用两个指针,同时从不同的方向遍历数组或链表,来解决一些特定的问题。

背景

双指针解题思路的产生背景主要有以下几个方面:

数据结构的特点

  • 许多需要解决的算法问题都涉及到对有序数组或链表等数据结构的操作。这类数据结构通常有一些特殊的性质,比如前后元素之间存在某种关系,这为使用双指针技术提供了可能。

问题的性质

  • 很多算法问题都涉及到在数组或链表中寻找、删除、插入、反转等操作。这些操作通常需要同时处理数据结构的两端或中间元素,双指针技术非常适合解决这类问题。

时间复杂度的要求

  • 许多实际问题都需要在线性时间内解决,双指针技术恰好能够在O(n)的时间复杂度内完成许多常见的操作,因此被广泛应用。

空间复杂度的需求

  • 相比于使用额外的数据结构,双指针技术通常只需要常量级的额外空间,这在一些对空间敏感的场景下非常有优势。

问题的分解

  • 一些复杂的问题可以通过引入双指针的方式进行适当的分解和简化,从而更容易找到最终的解决方案。

总之,双指针技术产生的背景主要源于算法问题的特点以及对时间复杂度和空间复杂度的需求。它是一种非常实用且高效的算法设计思路,在各种类型的算法问题中都有广泛的应用。掌握好这种技巧对于提高算法解题能力非常重要。

应用场景

双指针技术主要有以下几种常见的应用场景:

寻找数组/链表中的重复/unique元素

  • 例如:给定一个有序数组,去除其中的重复元素。可以使用双指针,一个指针遍历原数组,另一个指针指向要写入的位置,从而去重。
实例:
def removeDuplicates(nums):"""给定一个有序数组 nums ,在原地删除重复出现的元素,使每个元素只出现一次,返回删除后数组的新长度。"""if not nums:return 0# 使用两个指针i = 0for j in range(1, len(nums)):if nums[j] != nums[i]:i += 1nums[i] = nums[j]return i + 1

这个例子中,我们使用两个指针 i 和 j 来遍历数组。i 指针指向要写入的位置,而 j 指针用于遍历数组。当 nums[j] 与 nums[i] 不同时,我们将 nums[j] 写入 nums[i+1],并将 i 指针向前移动一位。这样就完成了数组去重的过程。

反转数组/链表

  • 例如:反转一个链表。可以使用两个指针,一个指向当前节点,另一个指向前一个节点,进行交换操作。
实例:
class ListNode:def __init__(self, val=0, next=None):self.val = valself.next = nextdef reverseList(head):"""反转一个单链表。"""prev, curr = None, headwhile curr:next_node = curr.nextcurr.next = prevprev = currcurr = next_nodereturn prev

在这个例子中,我们使用三个指针:prev、curr 和 next_node。prev 指针指向前一个节点,curr 指针指向当前节点,next_node 指针指向下一个节点。我们通过不断交换这三个指针的指向,最终反转整个链表。

寻找数组/链表中的中点

  • 例如:找出一个链表的中间节点。可以使用快慢两个指针,快指针每次移动两步,慢指针每次移动一步,这样当快指针到达末尾时,慢指针正好在中间。
实例:
def middleNode(head):"""给定一个头结点为 head 的非空单链表,返回链表的中间结点。如果有两个中间结点,则返回第二个中间结点。"""slow = fast = headwhile fast and fast.next:slow = slow.nextfast = fast.next.nextreturn slow

在这个例子中,我们使用了快慢两个指针。slow 指针每次移动一步,fast 指针每次移动两步。当 fast 指针到达链表末尾时,slow 指针正好指向链表的中间节点。这是一个经典的找链表中间节点的双指针算法。

寻找数组/链表中的目标元素

  • 例如:在一个有序数组中寻找目标元素。可以使用两个指针,分别指向数组的首尾,然后根据目标值与中间元素的大小关系,移动指针缩小搜索范围。
实例1:
def searchElement(nums, target):"""给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。"""left, right = 0, len(nums) - 1while left <= right:mid = left + (right - left) // 2if nums[mid] == target:return midelif nums[mid] < target:left = mid + 1else:right = mid - 1return left

这是一个在有序数组中查找目标元素的典型双指针算法,也就是二分查找。我们使用左右两个指针left和right来确定查找范围,然后不断缩小范围直到找到目标元素或确定目标元素不存在于数组中。

这种算法在数组中查找目标元素的时间复杂度为O(log n),非常高效。

实例二

类似地,我们也可以在链表中使用双指针来查找目标元素:

class ListNode:def __init__(self, val=0, next=None):self.val = valself.next = nextdef searchElementInList(head, target):"""给定一个链表和一个目标值,找到链表中第一个值等于该目标值的节点,并返回该节点的值。如果链表中不存在这样的节点,返回-1。"""slow, fast = head, headwhile fast:if fast.val == target:return fast.valslow = slow.nextfast = fast.nextreturn -1

在链表中查找目标元素,我们同样使用快慢两个指针来遍历链表。当fast指针找到目标元素时,我们就返回该元素的值。如果遍历完整个链表都没有找到目标元素,我们返回-1表示不存在。

滑动窗口问题

  • 例如:找出一个字符串中长度为k的最长无重复子串。可以使用两个指针表示滑动窗口的边界,通过移动指针来维护窗口内无重复元素。
实例:
def lengthOfLongestSubstring(s):"""给定一个字符串,找出其中不含有重复字符的最长子串的长度。"""left, right = 0, 0char_index = {}max_len = 0while right < len(s):if s[right] in char_index and char_index[s[right]] >= left:left = char_index[s[right]] + 1char_index[s[right]] = rightmax_len = max(max_len, right - left + 1)right += 1return max_len

在这个例子中,我们使用左右两个指针来维护一个滑动窗口。right 指针不断向右移动,而当遇到重复字符时,我们移动 left 指针来缩小窗口。这种方法可以高效地找到最长不重复子串。

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

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

相关文章

SonarWiz 8.0.1侧扫 浅剖 测深软件License获取

SonarWiz 8.0.1是功能强大的测绘软件&#xff01;提供强大的数据采集、后处理等功能操作&#xff0c;您将获得灵活完整的报告&#xff0c;并提供丰富的选项以便进行定制和更灵活的进行操作&#xff0c;软件功能齐全&#xff0c;包括完整的海底测绘解决方案&#xff0c;方便实时…

国产麒麟v10、UOS系统在线比较两个Word文件的内容差异

调用PageOffice的WordCompare方法&#xff0c;同时在线打开两个Word文档&#xff0c;可以切换显示其中的一个文档&#xff0c;或者显示两个文档的对比结果&#xff0c;即可实现在线的文档内容比较功能。此功能可以应用在以下方面&#xff1a; 文档管理中&#xff0c;比较两个版…

前端八股文 说一说样式优先级的规则是什么?

标准的回答 CSS样式的优先级应该分成四大类 第一类 !important&#xff1a; &#x1f604;无论引入方式是什么&#xff0c;选择器是什么&#xff0c;它的优先级都是最高的。 第二类 引入方式&#xff1a; &#x1f604;行内样式的优先级要高于嵌入和外链&#xff0c;嵌入和外链…

C++11|左、右值引用和移动语义

目录 一、左值引用 && 右值引用 1.1什么是左值&#xff1f;什么是左值引用&#xff1f; 1.2什么是右值&#xff1f;什么是右值引用&#xff1f; 1.3左值引用与右值引用比较 二、左、右值引用使用场景(为什么要有右值引用?) 2.1左值引用使用场景&#xff1a; 2.2右…

秋招突击——设计模式补充——简单工厂模式和策略模式

文章目录 引言正文简单工厂模式策略模式策略模式和工厂模式的结合策略模式解析 总结 引言 一个一个来吧&#xff0c;面试腾讯的时候&#xff0c;问了我单例模式相关的东西&#xff0c;自己这方面的东西&#xff0c;还没有看过。这里需要需要补充一下。但是设计模式有很多&…

数字IC设计-VCS和Verdi的使用

#学习记录# 前言&#xff1a;本文以一个简单的计数器来说明vcs和verdi的使用 1 代码文件 1.1 计数器代码 //Engineer&#xff1a;Mr-pn-junction module counter(input clk,input rst,output reg [5:0] count); always(posedge clk or negedge rst)beginif(!rst)coun…

【算法】(C语言):二分查找

二分查找&#xff1a; 获取查找区域的中间位置。若中间位置的数据就是要找的值&#xff0c;则返回true。若要找的值 小于 中间位置的数据&#xff0c;则往左边查找。若要找的值 大于 中间位置的数据&#xff0c;则往右边查找。重复1和2&#xff0c;若没有要找的值&#xff0c;…

LM-Cocktail:一种创新的模型合并方法打破预训练语言模型微调后的性能局限,实现语言模型在一般任务与特定领域的均衡高性能

LM-Cocktail:一种创新的模型合并方法打破预训练语言模型微调后的性能局限,实现语言模型在一般任务与特定领域的均衡高性能 使语言模型的微调类似于调制一杯精致的鸡尾酒。模型合并可用于提高单个模型的性能。我们发现此方法对于大型语言模型和密集嵌入模型也很有用,并设计了…

业界数据架构的演变

目录 一、概述 二、业务处理-单体架构 三、业务处理-微服务架构 四、数据分析-大数据Lambda架构 五、数据分析-Kappa架构 六、数据分析-LambdaKappa混合架构 七、湖仓一体架构 一、概述 近年来随着越来越多的大数据技术被开源&#xff0c;例如&#xff1a;HDFS、Spark等…

Linux 进程与服务管理:使用systemctl、service命令管理系统服务

Linux 进程与服务管理&#xff1a;使用systemctl、service命令管理系统服务 在Linux系统中&#xff0c;进程和服务的管理是系统运维的重要组成部分。随着Linux的发展&#xff0c;systemd成为了大多数现代Linux发行版的初始化系统&#xff0c;它提供了systemctl命令来管理系统服…

【Linux进程通信】共享内存

目录 共享内存函数 头文件 shmget ftok函数​ shmat shmdt shmctl 共享内存区是最快的IPC 形式。一旦这样的内存映射到共享它的进程的地址空间&#xff0c;这些进程间数据传递不再涉及到操作系统内核&#xff0c;换句话说是进程不再通过执行进入内核的系统调用来传递彼此的数据…

《向量数据库指南》——Milvus Cloud查询增强如何提升 RAG Pipeline 效果?

查询增强 1.假设性问题 这一方法首先使用LLM为每一条文档块生成一些假设问题,这些文档块可以回答与之对应的假设问题。在RAG阶段,进行一个query-to-query的搜索,先搜索到相关的假设问题,然后找到对应的文档块,再通过它们生成最后的回答。 这里的query-to-query指的是embed…

【数据库】仓库管理数据库(练习样例)

某连锁超市需要设计实现一个仓库管理系统&#xff0c;要求每个仓库可以有多名仓库管理员&#xff0c;每个仓库管理员只负责管理一个仓库&#xff0c;同时每个仓库都配备了一名仓库主管&#xff1b;不同的仓库存放的是不同类型的货品&#xff0c;每种货品只存放在固定的仓库中&a…

R语言数据分析案例40-华安中证基金多元线性回归和分析预测

一、引言 基金是一种非常受欢迎的投资工具&#xff0c;由于其具有风险分散、专业管理等特点&#xff0c;越来越多的投资者选择将资金投入基金市场。然而&#xff0c;基金的表现却受到很多因素的影响&#xff0c;如股票市场的整体表现、基金管理人员的能力、基金规模、基金管理…

SpringAOP如何控制只在生产环境执行AOP类

SpringAOP场景原理、如何在特定环境(如dev不执行、prod执行)不执行AOP 1、在定义AOP切面类的时候&#xff0c;用注解Profile&#xff0c; 再配合配置application.properties 的 spring.profiles.activeprod Aspect Component Profile("prod") public class LoggingA…

二二复制模式,发展下属并形成一个销售网络体系来实现收入增长!

二二复制模式&#xff0c;又称为双轨制&#xff0c;是一种直销理念的营销模式&#xff0c;其核心在于通过发展下属并形成一个销售网络体系来实现收入增长。以下是对二二复制模式的详细讲解&#xff0c;包括其优势和玩法介绍&#xff0c;以及适合的行业。 一、二二复制模式的定…

【React性能优化】父组件渲染如何避免子组件不必要的渲染

类组件&#xff1a; 需要注意的点是&#xff0c;尽量避免事件处理函数直接返回&#xff0c;如以下写法&#xff1a; class LoggingButton extends React.Component {handleClick() {console.log(this is:, this);}render() {// 此语法确保 handleClick 内的 this 已被绑定。r…

Flutter集成高德导航SDK(Android篇)(JAVA语法)

先上flutter doctor&#xff1a; flutter sdk版本为&#xff1a;3.19.4 引入依赖&#xff1a; 在app的build.gradle下&#xff0c;添加如下依赖&#xff1a; implementation com.amap.api:navi-3dmap:10.0.700_3dmap10.0.700navi-3dmap里面包含了定位功能&#xff0c;地图功能…

UML图的分类

uml图是用来展示大型软件工程之间的内部关系&#xff0c;如果只用代码表示&#xff0c;密密麻麻的谁看都头疼。 UML图分为结构图和行为图 .类图&#xff1a;一组对象&#xff0c;接口&#xff0c;协作和它们之间的关系 对象图&#xff1a;一组对象以及它们之间的关系 用例图…

Mysql在linux系统中定时备份

1. 创建备份脚本 #!/bin/bash# 设置数据库名、用户名和密码 DB_NAME"your_database_name" DB_USER"your_username" DB_PASSWORD"your_password"# 设置备份目录 BACKUP_DIR"/path/to/your/backup/directory"# 创建备份目录如果不存在 …