Leetcode算法入门与数组丨6. 数组双指针、滑动窗口

news2024/10/7 3:28:02

文章目录

    • 1 双指针基础知识
      • 1.1 双指针简介
      • 1.2 左右指针(对撞指针)
      • 1.3 快慢指针
      • 1.4 分离双指针
    • 2 滑动窗口基础知识
      • 2.1 滑动窗口算法介绍
      • 2.2 滑动窗口适用范围
      • 2.3 固定长度滑动窗口
      • 2.4 不固定长度滑动窗口

1 双指针基础知识

1.1 双指针简介

双指针(Two Pointers) 是一种常用的算法技巧,通常用于在数组或链表中进行遍历或搜索。它使用两个指针在不同的位置上移动,以解决特定的问题。

双指针常见的应用场景包括:

  1. 快慢指针:两个指针方向相同,但是使用两个指针以不同的速度遍历链表,用于解决链表中的环检测、链表中点、链表倒数第k个节点等问题。
  2. 左右指针(对撞指针):在有序数组中,使用两个指针从数组的两端向中间移动,以解决查找目标值、两数之和、三数之和等问题。
  3. 分离双指针:两个指针分别属于不同的数组/链表。

双指针算法通常具有较低的时间复杂度,并且可以在一次遍历中完成任务,因此在很多问题中都有很好的应用。

1.2 左右指针(对撞指针)

左右指针(Left and Right Pointers)是双指针算法中的一种常见技巧。它通常用于在有序数组或字符串中查找目标值、寻找满足某种条件的子序列等问题。

左右指针的基本思想是,使用两个指针分别指向数组或字符串的左右两端,然后根据问题的要求,通过移动指针的位置来逼近或搜索目标。

基本步骤

  1. 两个指针 l e f t left left r i g h t right right l e f t left left 指向序列第一个元素( l e f t = 0 left=0 left=0 ), r i g h t right right 指向序列最后一个元素( r i g h t = l e n ( n u m s ) − 1 right=len(nums)-1 right=len(nums)1 )。
  2. 循环体中将左右指针相向移动,当满足一定条件时,将左指针右移( l e f t + = 1 left += 1 left+=1 )。当满足另外一定条件时,将右指针左移( r i g h t + = 1 right+= 1 right+=1 )。
  3. 直到两个指针相撞( l e f t = r i g h t left=right left=right ),或者满足其他要求的特殊条件时,跳出循环体。

伪代码模板

left, right = 0, len(nums) - 1

while left < right:
    if 满足要求的特殊条件:
        return 符合条件的值 
    elif 一定条件 1:
        left += 1
    elif 一定条件 2:
        right -= 1

return 没找到 或 找到对应值

适用范围

  1. 有序数组或有序链表:左右指针可以在有序数组或有序链表中进行查找、搜索、比较等操作。通过左右指针的移动,可以快速定位目标值或满足特定条件的元素。

  2. 滑动窗口问题:滑动窗口问题通常涉及在数组或字符串上定义一个窗口,并通过移动窗口的左右边界来解决问题。左右指针可以用于表示窗口的左右边界,并根据问题的要求进行移动和调整。

  3. 两数之和、三数之和等问题:在有序数组中查找满足特定条件的数对或数组合时,左右指针可以进行逼近,快速找到满足条件的解。

  4. 回文字符串判断:左右指针可以用于判断字符串是否是回文字符串。通过左右指针从两端向中间移动,并比较对应位置的字符是否相等,可以判断字符串是否是回文。

1.3 快慢指针

快慢指针(Fast and Slow Pointers)基本思想是使用两个指针,一个指针(快指针 fast)移动速度较快,另一个指针(慢指针 slow)移动速度较慢。通过两个指针的相对移动,可以得到一些有用的信息,从而解决问题。

基本步骤

  1. 初始化快慢指针:将快指针和慢指针都指向链表的头节点或数组的起始位置。 s l o w = 0 , f a s t = 1 或 0 slow=0, fast=1 或 0 slow=0,fast=10

  2. 移动指针:根据问题的要求,通过移动快慢指针来逼近目标或获取有用的信息。

    • 快指针移动:通常每次移动两步或一步,可以快速遍历整个链表或数组。 f a s t + = 1 fast +=1 fast+=1
    • 慢指针移动:通常每次移动一步,慢指针的移动速度较慢。 s l o w + = 1 slow+=1 slow+=1
  3. 判断终止条件:根据问题的要求,判断是否满足终止条件。

    • 例如,在链表中环检测问题中,如果快指针和慢指针相遇,则存在环;如果快指针到达链表末尾,则不存在环。
  4. 根据问题的要求返回结果。

    • 例如,在链表中找到中间节点的问题中,当快指针到达链表末尾时,慢指针所指的节点就是中间节点。

伪代码模板

slow = 0
fast = 1
while 没有遍历完:
    if 满足要求的特殊条件:
        slow += 1
    fast += 1
return 合适的值

适用范围

  1. 链表中的环检测:快慢指针可以用于判断链表中是否存在环。快指针每次移动两步,慢指针每次移动一步,如果链表中存在环,则快指针最终会追上慢指针,二者相遇。

  2. 链表中点的查找:快慢指针可以用于找到链表的中间节点。快指针每次移动两步,慢指针每次移动一步,当快指针到达链表末尾时,慢指针正好在链表的中间位置。

  3. 链表倒数第k个节点的查找:快慢指针可以用于定位链表的倒数第k个节点。快指针先移动k个位置,然后快慢指针同时向前移动,当快指针到达链表末尾时,慢指针所指的节点就是倒数第k个节点。

  4. 判断链表是否有交点:快慢指针可以用于判断两个链表是否相交。分别使用快慢指针遍历两个链表,如果两个链表相交,则快指针和慢指针最终会相遇。

  5. 数组中的重复元素查找:快慢指针可以用于在数组中查找重复元素。通过快慢指针的移动,可以找到数组中的重复元素或判断数组是否存在重复元素。

1.4 分离双指针

分离双指针(Two Pointers with Separation)是一种双指针算法的变体,它通常用于解决数组或链表中需要分离的问题,例如将奇偶数分离、将0和非0元素分离等。分离双指针的基本思想是使用两个指针,一个指针(分离指针)用于分离元素,另一个指针(遍历指针)用于遍历数组或链表。通过移动遍历指针,并根据特定条件将元素交换到分离指针的位置,实现元素的分离。

基本步骤

  1. 两个指针 l e f t 1 left_1 left1 l e f t 2 left_2 left2 l e f t 1 left_1 left1 指向第一个数组的第一个元素( l e f t 1 = 0 left_1=0 left1=0), l e f t 2 left_2 left2 指向第二个数组的第一个元素( l e f t 2 = 0 left_2=0 left2=0)。

  2. 当满足一定条件时

    1. 两个指针同时右移, l e f t 1 + = 1 、 l e f t 2 + = 1 left_1 += 1、left_2 += 1 left1+=1left2+=1

    2. l e f t 1 left_1 left1 右移, l e f t 1 + = 1 left_1 += 1 left1+=1

    3. l e f t 2 left_2 left2 右移, l e f t 2 + = 1 left_2 += 1 left2+=1

  3. 当其中一个数组遍历完时或者满足其他特殊条件时跳出循环体。

伪代码模板

left_1 = 0
left_2 = 0

while left_1 < len(nums1) and left_2 < len(nums2):
    if 一定条件 1:
        left_1 += 1
        left_2 += 1
    elif 一定条件 2:
        left_1 += 1
    elif 一定条件 3:
        left_2 += 1

适用范围

分离双指针一般用于处理有序数组合并,求交集、并集问题

349. 两个数组的交集 - 力扣(LeetCode)

在这里插入图片描述

思路:分离双指针

  1. 两个数组先排序。( n u m s 1 、 n u m s 2 nums1、nums2 nums1nums2
  2. 使用两个指针分别指向两个数组的第一个元素,即第一个指针指向第一个数组的第一个元素,第二个指针指向第二个数组的第一个元素。( l e f t 1 = 0 、 l e f t 2 = 0 left_1=0、left_2=0 left1=0left2=0
  3. 如果 n u m s 1 [ l e f t 1 ] = = n u m s 2 [ l e f t 2 ] nums1[left_1]==nums2[left_2] nums1[left1]==nums2[left2] 则将其加入答案数组(注意去重),并将 l e f t 1 left_1 left1 l e f t 2 = 0 left_2=0 left2=0 右移 ( l e f t 1 + = 1 left_1 += 1 left1+=1 l e f t 2 + = 1 left_2 += 1 left2+=1)。
  4. 如果 n u m s 1 [ l e f t 1 ] < n u m s 2 [ l e f t 2 ] nums1[left_1] < nums2[left_2] nums1[left1]<nums2[left2] ,则 l e f t 1 left_1 left1 右移 。
  5. 如果 n u m s 1 [ l e f t 1 ] > n u m s 2 [ l e f t 2 ] nums1[left_1] > nums2[left_2] nums1[left1]>nums2[left2] ,则 l e f t 2 left_2 left2 右移 。
  6. 返回答案。

代码

class Solution:
    def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]:
        nums1.sort()
        nums2.sort()

        left_1 = 0
        left_2 = 0
        res = []
        while left_1 < len(nums1) and left_2 < len(nums2):
            if nums1[left_1] == nums2[left_2]:
                if nums1[left_1] not in res:
                    res.append(nums1[left_1])
                left_1 += 1
                left_2 += 1
            elif nums1[left_1] < nums2[left_2]:
                left_1 += 1
            elif nums1[left_1] > nums2[left_2]:
                left_2 += 1
        return res

在这里插入图片描述

2 滑动窗口基础知识

2.1 滑动窗口算法介绍

滑动窗口算法(Sliding Window Algorithm)是一种常用的算法技巧,用于解决数组或字符串相关的问题。它的基本思想是维护一个窗口,通过在数组或字符串上滑动窗口,不断更新窗口内的状态,从而解决问题。可以对窗口进行滑动操作、缩放操作,以及维护最优解操作。

  • 滑动操作:窗口向一个方向移动,常见的是向右侧移动。
  • 缩放操作:针对不固定长度的窗口,可以从左侧缩小窗口长度,也可以从右侧扩大窗口长度。

2.2 滑动窗口适用范围

滑动窗口算法通常适用于线性数据结构,如数组和字符串。它在处理大规模数据时特别有用,因为它可以通过滑动窗口的方式,仅对部分数据进行处理,而不需要遍历整个数据集。这使得滑动窗口算法具有较低的时间复杂度,并且在实际应用中常常能够提供高效的解决方案。

按照窗口长度的固定情况,我们可以将滑动窗口题目分为以下两种:

  • 固定长度窗口:窗口大小是固定的。

  • 不定长度窗口

    • 窗口大小是不固定的。

    • 求解最大的满足条件的窗口。

    • 求解最小的满足条件的窗口。

2.3 固定长度滑动窗口

固定长度滑动窗口是一种特殊的滑动窗口算法,它的窗口大小固定,不会随着数据集的变化而变化。该算法通常用于需要对连续的数据序列进行处理的问题,如时间序列数据分析等。

固定长度滑动窗口的基本思想是:将数据序列分成若干个固定长度的子序列,然后对每个子序列进行处理。在处理每个子序列时,可以使用滑动窗口算法,通过移动窗口来更新子序列内的状态,从而得到最终的结果。

基本步骤

固定长度滑动窗口的步骤如下:

  1. 初始化窗口的起始位置和结束位置,窗口大小固定。
  2. 对于每个窗口内的子序列,根据问题要求更新窗口内的状态。
  3. 如果窗口满足特定条件,记录结果。
  4. 继续移动窗口的起始位置,重复上述步骤,直到遍历完整个数据集。

代码模板

left = 0
right = 0

while right < len(nums):
    window.append(nums[right])
    
    # 超过窗口大小时,缩小窗口,维护窗口中始终为 window_size 的长度
    if right - left + 1 >= window_size:
        # ... 维护答案
        window.popleft()
        left += 1
    
    # 向右侧增大窗口
    right += 1

2.4 不固定长度滑动窗口

不定长度滑动窗口算法(Sliding Window):在给定数组 / 字符串上维护一个不定长度的窗口。可以对窗口进行滑动操作、缩放操作,以及维护最优解操作。

基本步骤

  1. 使用两个指针 l e f t left left r i g h t right right。初始时, l e f t left left r i g h t right right 都指向序列的第一个元素。即: l e f t = 0 left=0 left=0 r i g h t = 0 right=0 right=0,区间 [ l e f t , r i g h t ] [left,right] [left,right] 被称为一个「窗口」。
  2. 将区间最右侧元素添加入窗口中,即 window.add(s[right])
  3. 然后向右移动 r i g h t right right,从而增大窗口长度,即 right += 1。直到窗口中的连续元素满足要求。
  4. 此时,停止增加窗口大小。转向不断将左侧元素移出窗口,即 window.popleft(s[left])
  5. 然后向右移动 l e f t left left,从而缩小窗口长度,即 left += 1。直到窗口中的连续元素不再满足要求。
  6. 重复 2 ~ 5 步,直到 r i g h t right right 到达序列末尾。

代码模板

left = 0
right = 0

while right < len(nums):
    window.append(nums[right])
    
    while 窗口需要缩小:
        # ... 可维护答案
        window.popleft()
        left += 1
    
    # 向右侧增大窗口
    right += 1

参考文献

  • [1] https://datawhalechina.github.io/leetcode-notes/#/

—— END ——


如果以上内容有任何错误或者不准确的地方,欢迎在下面 👇 留言。或者你有更好的想法,欢迎一起交流学习~~~

更多精彩内容请前往 AXYZdong的博客

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

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

相关文章

LeetCode算法心得——美丽塔 I(HashMap)

大家好&#xff0c;我是晴天学长&#xff0c;hashmap的灵活应用&#xff0c;然后边界的细节处理&#xff0c;需要的小伙伴可以关注支持一下哦&#xff01;后续会继续更新的。 1) .美丽塔 美丽塔 I 给你一个长度为 n 下标从 0 开始的整数数组 maxHeights 。 你的任务是在坐标轴…

SpringBoot项目(百度AI整合)——如何在Springboot中使用文字识别OCR入门

前言 前言&#xff1a;本系列博客尝试结合官网案例&#xff0c;阐述百度 AI 开放平台里的组件使用方式&#xff0c;核心是如何在spring项目中快速上手应用。 本文介绍如何在Springboot中使用百度AI的文字识别OCR 其他相关的使用百度AI的文章列表如下&#xff1a; 如何在Spri…

【SpringBoot】-SpringBoot配置文件

作者&#xff1a;学Java的冬瓜 博客主页&#xff1a;☀冬瓜的主页&#x1f319; 专栏&#xff1a;【Framework】 主要内容&#xff1a;.properties 配置文件和 .yml 配置文件中 配置信息的设置和获取。关于IDEA乱码的解决。.yml 配置文件的 方式语法分析和演示。 .yml配置文件 …

芯片验证就是一次旅行

如果你国庆希望去一个你不曾去过的城市旅行&#xff0c;比如“中国苏州”。对游客来说&#xff0c;它是个蛮大的城市&#xff0c;有许多景点可以游玩&#xff0c;还有许多事情可以做。但实际上&#xff0c;即使最豪也最清闲的游客也很难看苏州的所有方方面面。同样的道理也适用…

第 4 章 串(文本行编辑实现)

1. 背景说明 该文本编辑器利用串的堆实现&#xff0c;其中对串的原始存储方式基本不作修改&#xff08;有部分修改之处&#xff09;&#xff0c;优化之处在于在串的末尾加上了一个空字符&#xff0c;目的是区分字符串结尾&#xff0c;便于将串保存在文件中&#xff0c;且该优化…

JavaScript入门——(2)基础语法(上)

一、JavaScript介绍 1.1 JavaScript是什么 1.1.1 JavaScript是什么&#xff1f; JavaScript是一种运行在客户端&#xff08;浏览器&#xff09;的编程语言&#xff0c;实现人机交互效果。 注意&#xff1a;HTML和CSS是标记语言。 1.1.2 作用&#xff08;做什么&#xff1f…

8月最新修正版风车IM即时聊天通讯源码+搭建教程

8月最新修正版风车IM即时聊天通讯源码搭建教程。风车 IM没啥好说的很多人在找,IM的天花板了,知道的在找的都知道它的价值,开版好像就要29999,后端加密已解,可自己再加密,可反编译出后端项目源码,已增加启动后端需要google auth双重验证,pc端 web端 wap端 android端 ios端 都有 …

小米机型解锁bl 跳“168小时”限制 操作步骤分析

写到前面的安全提示 了解解锁bl后的风险&#xff1a; 解锁设备后将允许修改系统重要组件&#xff0c;并有可能在一定程度上导致设备受损&#xff1b;解锁后设备安全性将失去保证&#xff0c;易受恶意软件攻击&#xff0c;从而导致个人隐私数据泄露&#xff1b;解锁后部分对系…

老胡的周刊(第109期)

老胡的信息周刊[1]&#xff0c;记录这周我看到的有价值的信息&#xff0c;主要针对计算机领域&#xff0c;内容主题极大程度被我个人喜好主导。这个项目核心目的在于记录让自己有印象的信息做一个留存以及共享。 &#x1f3af; 项目 lobe-chat[2] LobeChat 是一个开源的、可扩展…

面试题:说说Java并发运行中的一些安全问题

文章目录 1.什么是多线程并发运行安全问题&#xff1f;2.用synchronized修饰的方法3.同步块4.使用Synchronized修饰静态方法5.互斥锁6.死锁现象7.wait()和sleep()的区别 1.什么是多线程并发运行安全问题&#xff1f; 当多个线程并发操作一个数据时&#xff0c;由于线程操作的时…

在Linux上安装Percona Toolkit工具

安装步骤 1. 下载安装包 下载地址&#xff1a;https://www.percona.com/software/database-tools/percona-toolkit 2.上传并解压 上传tar包到服务器&#xff0c;并通过tar -zxvf 文件名.tar.gz解压。工具在bin文件夹中&#xff0c;这个是免安装的。 3. 配置环境变量 配置…

安装OpenSearch

title: “安装opensearch” createTime: 2021-11-30T19:13:4508:00 updateTime: 2021-11-30T19:13:4508:00 draft: false author: “name” tags: [“es”,“安装”] categories: [“OpenSearch”] description: “测试的” 说明 基于Elasticsearch7.10.2 的 opensearch-1.1.…

gRPC之实现TLS通信加密_已设置图床

gRPC之实现TLS通信加密 "crypto/tls"包 “crypto/tls” 是 Go 编程语言中的一个包&#xff0c;用于实现 TLS&#xff08;传输层安全&#xff09;协议。TLS 协议用于加密和保护网络通信&#xff0c;通常用于保护敏感数据的传输&#xff0c;如密码、支付信息等。在 G…

详解--计算机存储相关(寄存器、CPU Cache、内存、外存)

CPU寄存器、高速缓冲存储器、主存储器、外存储器 1. 主存储器 参考链接–主存 参考链接–内存 主存储器简称 主存&#xff0c;又称 内存储器&#xff08;简称 内存&#xff09;。作用 暂时存放CPU中的运算数据。存放指令和数据&#xff0c;并能由中央处理器&#xff08;CPU&a…

什么是Service Worker?它在PWA中的作用是什么?

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ Service Worker的作用是什么&#xff1f;⭐ 写在最后 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 欢迎来到前端入门之旅&#xff01;感兴趣的可以订阅本专栏哦&#xff01;这个专栏是为那些对Web开发感兴趣、刚刚踏入前…

1066 二级C语言-自定义函数

输入一个正数x和一个正整数n&#xff0c;求下列算式的值。要求定义两个调用函数&#xff1a; &#xff08;1&#xff09;fact(n)计算n的阶乘&#xff1b; &#xff08;2&#xff09;mypow(x,n)计算x的n次幂&#xff08;即xn&#xff09;&#xff0c;两个函数的返回值类型是do…

HEC-HMS和HEC-RAS水文模型、防洪评价报告编制及洪水建模、洪水危险性评价等相关案例解析

► HEC-RAS一维、二维建模方法及应用 【目标】&#xff1a; 1.掌握一维数学模型基本地形导入方法 2.掌握恒定流、非恒定流一维数学模型水流计算方法 3.掌握一维数学模型计算结果分析&#xff0c;水面线成果分析及调试&#xff1b;流速分布图输出方法 4.掌握一维数学模型增设构…

如何让一个uniform variable在多级shader中都起作用(类似C语言的全局变量)?

GLSL编程中通常设计多个shader&#xff0c;如vertex shader, fragment shader等等。在最近的某个项目中&#xff0c;我需要定义一个变量&#xff0c;该变量类似C语言中的全局变量&#xff0c;要同时在两个shader中都起作用。c - OpenGL Uniform Across Multiple Shaders - Stac…

2023-9-23 区间选点

题目链接&#xff1a;区间选点 #include <iostream> #include <algorithm>using namespace std;const int N 100010;int n;struct Range {int l, r;bool operator< (const Range &W) const{return r < W.r;} }range[N];int main() {scanf("%d"…

MyBatisPlus + ShardingJDBC 批量插入不返回主键ID

本文讲述一个由 ShardingJDBC 使用不当引起的悲惨故事。 一. 问题重现 有一天运营反馈我们部分订单状态和第三方订单状态无法同步。 根据现象找到了不能同步订单状态是因为 order 表的 thirdOrderId 为空导致的&#xff0c;但是这个字段为啥为空&#xff0c;排查过程比较波折…