【优选算法】—— 滑动窗口类问题

news2024/11/24 5:39:49

本期,我给大家带来的是关于滑动窗口类算法的介绍,并通过具体的题目帮助大家思考理解。

c语言滑动窗口算法 - CSDN 

 

目录

(一)基本概念

(二)题目讲解

1、难度:medium

1️⃣长度最小的子数组

2️⃣找到字符串中所有字⺟异位词

2、难度:hard

1️⃣最⼩覆盖⼦串

2️⃣串联所有单词的⼦串

总结


(一)基本概念

滑动窗口算法(Sliding Window Algorithm)是一种常见的算法技巧,用于解决一些数组或字符串相关的问题。该算法基于窗口的概念,在一个固定大小的窗口内移动,并根据具体问题进行适当的调整和计算。

下面将详细的介绍滑动窗口算法的工作原理和应用场景:

工作原理

  • 窗口大小:滑动窗口算法通过设定一个窗口的大小来解决问题。窗口通常是一个连续的子数组或子字符串。
  • 初始化窗口:初始化窗口的起始位置,并根据问题需求设定窗口的大小。
  • 移动窗口:通过移动窗口的起始位置,不断调整窗口的大小和位置,以找到满足问题条件的解。
  • 更新解:根据窗口的移动和调整,更新问题的解,并记录或返回所需的结果。

应用场景:

  • 最小/最大子数组/子字符串:寻找给定数组或字符串中满足特定条件的最小或最大的子数组或子字符串。
  • 字符串匹配:在一个字符串中寻找另一个字符串的出现或满足特定条件的子串。
  • 滑动窗口和哈希表结合:通过使用哈希表来优化滑动窗口算法,提高效率。
  • 优化窗口大小:根据问题的特性,调整窗口大小以寻找最佳解。

滑动窗口算法的步骤通常如下:

  1. 初始化窗口的起始位置和结束位置,使其满足问题的要求。
  2. 进入循环,不断移动窗口的起始位置和结束位置,直到窗口滑动到数组或字符串的末尾。
  3. 在每一次循环中,检查窗口内的元素是否满足问题的要求。如果满足条件,则更新解或执行其他操作。如果不满足条件,则继续移动窗口。
  4. 在移动窗口时,要更新窗口内的元素和相应的数据结构,以确保窗口的正确性。
  5. 重复步骤2到步骤4,直到遍历完整个数组或字符串,返回解或所需的结果。


(二)题目讲解

接下来,我们通过几道题目让大家具体的感受一下。(题目由易到难

1、难度:medium

1️⃣长度最小的子数组

题⽬链接:209. 长度最小的子数组

 【题⽬描述】

【解法】(滑动窗⼝

算法思路:
由于此问题分析的对象是「⼀段连续的区间」,因此可以考虑「滑动窗」的思想来解决这道题。
让滑动窗⼝满⾜:从 i 位置开始,窗⼝内所有元素的和⼩于target (那么当窗内元素之和
第⼀次⼤于等于⽬标值的时候,就是 i 位置开始,满⾜条件的最⼩⻓度)。


做法:将右端元素划⼊窗⼝中,统计出此时窗⼝内元素的和:

  • 如果窗⼝内元素之和⼤于等于 target :更新结果,并且将左端元素划出去的同时继续判断是否满⾜条件并更新结果(因为左端元素可能很⼩,划出去之后依旧满⾜条件)
  • 如果窗⼝内元素之和不满⾜条件: right++ ,另下⼀个元素进⼊窗⼝。
     

【原理解释】

很多题解以及帖⼦只给你说怎么做,没给你解释为什么这么做。为何滑动窗⼝可以解决问题,并且时间复杂度更低?


▪ 这个窗⼝寻找的是:以当前窗⼝最左侧元素(记为 left1 )为基准,符合条件的情况。也
就是在这道题中,从 left1 开始,满⾜区间和 sum >= target 时的最右侧(记为
right1 )能到哪⾥。


▪ 我们既然已经找到从 left1 开始的最优的区间,那么就可以⼤胆舍去 left1 。但是如
果此时重新开始统计第⼆个元素( left2 )往后的和,势必会有⼤量重复的计算(因为我们在求第⼀段区间的时候,已经算出很多元素的和了,这些和是可以在计算下次区间和的时候⽤上的)。


▪ 此时, rigth1 的作⽤就体现出来了,我们只需将 left1 这个值从 sum 中剔除。从
right1 这个元素开始,往后找满⾜ left2 元素的区间(此时 right1 也有可能是满⾜的,因为 left1 可能很⼩。 sum 剔除掉 left1 之后,依旧满⾜⼤于等于target )。这样我们就能省掉⼤量重复的计算。


▪ 这样我们不仅能解决问题,⽽且效率也会⼤⼤提升
 

【算法实现】

class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        int n = nums.size(), sum = 0, len = INT_MAX;
        for(int left = 0, right = 0; right < n; right++)
        {
            sum += nums[right]; // 进窗⼝
            while(sum >= target) // 判断
            {
                len = min(len, right - left + 1); // 更新结果
                sum -= nums[left++]; // 出窗⼝
            }
        }
    return len == INT_MAX ? 0 : len;
    }
};

【结果展示】

 【性能分析】

 具体的性能分析如下:

  • 时间复杂度:因为每个元素最多被遍历两次(进窗口和出窗口),时间复杂度是线性的。所以时间复杂度为O(n),其中n是数组nums的长度。
  • 空间复杂度:代码中只使用了几个额外的变量,不随输入规模变化,所以空间复杂度为O(1)

 

2️⃣找到字符串中所有字⺟异位词

题⽬链接:438. 找到字符串中所有字母异位词

 

【题⽬描述】

【解法】(滑动窗⼝+ 哈希表)


算法思路:

  1. 因为字符串 p 的异位词的⻓度⼀定与字符串 p 的⻓度相同,所以我们可以在字符串 s 中构造⼀个⻓度为与字符串 p 的⻓度相同的滑动窗⼝,并在滑动中维护窗⼝中每种字⺟的数量;
  2. 当窗⼝中每种字⺟的数量与字符串 p 中每种字⺟的数量相同时,则说明当前窗⼝为字符串 p 的异位词;
  3.  因此可以⽤两个⼤⼩为 26 的数组来模拟哈希表,⼀个来保存 s 中的⼦串每个字符出现的个数,另⼀个来保存 p 中每⼀个字符出现的个数。这样就能判断两个串是否是异位词。

【算法实现】

class Solution {
public:
    vector<int> findAnagrams(string s, string p) {
        vector<int> ret;
        int hash1[26] = { 0 }; // 统计字符串 p 中每个字符出现的个数
        for(auto ch : p) 
            hash1[ch - 'a']++;
        int hash2[26] = { 0 }; // 统计窗⼝⾥⾯的每⼀个字符出现的个数
        int m = p.size();
        for(int left = 0, right = 0, count = 0; right < s.size(); right++)
        {
            char in = s[right];
            // 进窗⼝ + 维护 count
            if(++hash2[in - 'a'] <= hash1[in - 'a']) 
                count++;
            if(right - left + 1 > m) // 判断
            {
                char out = s[left++];
                // 出窗⼝ + 维护 count
                if(hash2[out - 'a']-- <= hash1[out - 'a']) 
                    count--;
            }
            // 更新结果
            if(count == m) 
                ret.push_back(left);
        }
        return ret;
    }
};

【结果展示】

 【性能分析】

具体的性能分析如下:

  • 时间复杂度:代码中使用了双指针来遍历字符串s,所以时间复杂度为O(n),其中n是字符串s的长度。
  • 空间复杂度:代码中使用了两个大小为26的哈希数组,所以空间复杂度为O(1)

2、难度:hard

1️⃣最⼩覆盖⼦串

链接如下:76. 最小覆盖子串

【题⽬描述】

 

【解法】 (滑动窗⼝+哈希表

算法思路:
◦ 研究对象是连续的区间,因此可以尝试使⽤滑动窗⼝的思想来解决。
◦ 如何判断当前窗⼝内的所有字符是符合要求的呢?

  • 我们可以使⽤两个哈希表,其中⼀个将⽬标串的信息统计起来,另⼀个哈希表动态的维护窗⼝内字符串的信息。
  • 当动态哈希表中包含⽬标串中所有的字符,并且对应的个数都不⼩于⽬标串的哈希表中各个字符的个数,那么当前的窗⼝就是⼀种可⾏的⽅案

【算法流程】

a. 定义两个全局的哈希表: 1 号哈希表 hash1 ⽤来记录⼦串的信息, 2 号哈希表 hash2 ⽤来记录⽬标串 t 的信息;


b.  实现⼀个接⼝函数,判断当前窗⼝是否满⾜要求:
    i. 遍历两个哈希表中对应位置的元素:
        • 如果 t 中某个字符的数量⼤于窗⼝中字符的数量,也就是 2 号哈希表某个位置⼤于1 号哈希表。说明不匹配,返回 false ;
        • 如果全都匹配,返回 true

主函数中:
a. 先将 t 的信息放⼊ 2 号哈希表中;
b. 初始化⼀些变量:左右指针: left = 0,right = 0 ;⽬标⼦串的⻓度: len =
INT_MAX ;⽬标⼦串的起始位置: retleft ;(通过⽬标⼦串的起始位置和⻓度,我们就
能找到结果)
c. 当 right ⼩于字符串 s 的⻓度时,⼀直下列循环:

  • i. 将当前遍历到的元素扔进 1 号哈希表中;
  • ii. 检测当前窗⼝是否满⾜条件:

• 如果满⾜条件:
        ◦ 判断当前窗⼝是否变⼩。如果变⼩:更新⻓度 len ,以及字符串的起始位置
retleft ;
        ◦ 判断完毕后,将左侧元素滑出窗⼝,顺便更新 1 号哈希表;
        ◦ 重复上⾯两个过程,直到窗⼝不满⾜条件;

  • iii. right++ ,遍历下⼀个元素;

d. 判断 len 的⻓度是否等于 INT_MAX :

  • i. 如果相等,说明没有匹配,返回空串;
  • ii. 如果不想等,说明匹配,返回 s 中从 retleft 位置往后 len ⻓度的字符串。

【算法实现】

class Solution {
public:
    string minWindow(string s, string t) {
        int hash1[128] = { 0 }; // 统计字符串 t 中每⼀个字符的频次
        int kinds = 0; // 统计有效字符有多少种
        for(auto ch : t)
        if(hash1[ch]++ == 0) kinds++;
        int hash2[128] = { 0 }; // 统计窗⼝内每个字符的频次
        int minlen = INT_MAX, begin = -1;
        for(int left = 0, right = 0, count = 0; right < s.size(); right++)
        {
            char in = s[right];
            if(++hash2[in] == hash1[in]) count++; // 进窗⼝ + 维护 count
            while(count == kinds) // 判断条件
            {
                if(right - left + 1 < minlen) // 更新结果
                {
                    minlen = right - left + 1;
                    begin = left;
                }
                char out = s[left++];
                if(hash2[out]-- == hash1[out]) count--; // 出窗⼝ + 维护 count
            }
        }
        if(begin == -1) 
            return "";
        else 
            return s.substr(begin, minlen);
    }
};

【结果展示】

 

【性能分析】

具体的性能分析如下:

  • 时间复杂度:算法的时间复杂度为O(n),n为字符串s的长度。代码使用哈希表记录频次信息,实现了线性时间复杂度。
  • 空间复杂度:哈希表hash1和hash2的存储空间为128个元素的数组,因此空间复杂度为O(1)


2️⃣串联所有单词的⼦串

链接如下:30. 串联所有单词的子串

【题⽬描述】

 

 【解法】

算法思路:


如果我们把每⼀个单词看成⼀个⼀个字⺟,问题就变成了找到「字符串中所有的字⺟异位词」。⽆
⾮就是之前处理的对象是⼀个⼀个的字符,我们这⾥处理的对象是⼀个⼀个的单词。
 

因此,这道题就留给大家自己做了!!


总结

以上就是本期关于滑动窗口算法的主要内容讲解了。感谢大家的观看与支持!!!

 

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

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

相关文章

Xcode 更新后 Version 14.3.1报错

File not found: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/arc/libarclite_iphonesimulator.a Xode版本和pod版本不一致导致&#xff0c;改成一致就可以了 放在podfile文件里,最后一个pod,和最后一个end中间 Showing Recent …

将字符串转换为字典json.loads(字符串)

【小白从小学Python、C、Java】 【计算机等考500强证书考研】 【Python-数据分析】 将字符串转换为字典 json.loads(字符串) [太阳]选择题 关于以下代码说法错误的是? import json myStr{"name":"xiaobai","age":30} print(【显示】mySt…

【论文阅读】TransCAM: Transformer Attention-based CAM Refinement for WSSS

分享一篇阅读的用于弱监督分割的论文 论文标题&#xff1a; TransCAM: Transformer Attention-based CAM Refinement for Weakly Supervised Semantic Segmentation 作者信息&#xff1a; 代码地址&#xff1a; https://github.com/liruiwen/TransCAM Abstract 大多数现有…

java判断某个字符串是否在字符串数组中的方法(4种)

1.效率最高&#xff08;最原始&#xff09; 代码如下&#xff08;示例&#xff09;&#xff1a; public class Demo { public static boolean useLoop(String[] arr, String targetValue) { for (String s : arr) { if (s.equals(targetValue)) return true; } return false; }…

图像分类——模型微调

目录 微调热狗识别获取数据集模型构建与训练 微调 热狗识别 获取数据集 import tensorflow as tf import pathlibtraindirhotdog/train testdirhotdog/test image_gentf.keras.preprocessing.image.ImageDataGenerator(rescale1/255) train_data_genimage_gen.flow_from_direc…

2023年Q2空调行业品牌数据榜单(京东商品数据)

随着夏季的来临&#xff0c;高温天气也带动部分家电行业的销售&#xff0c;以空调为代表的家电市场正逐步恢复活力。结合鲸参谋电商数据分析平台的相关数据&#xff0c;我们来分析一下2023年Q2空调市场的具体销售表现。 根据鲸参谋平台的数据显示&#xff0c;2023年4-6月份&am…

TypeScript学习(2)- ts基本类型

类型声明式Ts非常重要的一个特点&#xff0c;通过类型声明可以指定TS中变量&#xff08;参数&#xff0c;形参&#xff09;的类型。指定类型后&#xff0c;当为变量赋值时&#xff0c;ts编译器会自动检查值是否符合类型声明&#xff0c;符合则赋值&#xff0c;否则报错。 目录 …

从VAE到Diffusion生成模型详解(1):VAE

文章目录 1. 生成式模型简介2. PixelRNN/CNN3. VAE3.1 自编码器AE3.2 VAE基本原理3.3 VAE公式推导 4. 参考 1. 生成式模型简介 什么是生成式模型 给定训练集&#xff0c;产生与训练集同分布的新样本。如下图所示&#xff0c;希望学习到一个模型 p m o d e l ( x ) p_{model}(…

22 分页控件

文章目录 界面设置初始化主对话框子页面初始化 页面1枚举窗口页面2枚举进程全部代码 界面设置 ui 设置 >创建CTablCtrl > 创建页控件&#xff08;子窗口&#xff09;&#xff0c;style设置成为chlid 添加类 页面中加入listCtrl 控件 添加变量 分别添加初始化函数 初始化…

举例说明单层神经网络的工作原理

假设我们有一个简单的单层神经网络&#xff0c;用于解决一个简单的问题&#xff1a;根据一个人的年龄&#xff08;x&#xff09;来预测其收入&#xff08;y&#xff09;。在这个例子中&#xff0c;输入数据只有一个特征&#xff08;年龄&#xff09;&#xff0c;因此我们可以用…

Can转以太网网关can协议转以太网协议

YC-ETH-CAN-2 是一款用来把 CAN 总线数据转为网口数据的设备。网口支持 TCP Sever、TCP Client、UDP Sever、UDP Client、UDP Broadcast 模式&#xff0c;可以通过软件配置和网页配置。 设备提供两路 CAN 接口&#xff0c;两路 CAN 可分别配置为不同的工作模式&#xff0c;独立…

[QT编程系列-4]:C++图形用户界面编程,QT框架快速入门培训 - 2- QT程序的运行框架:信号、槽函数、对象之间的通信

目录 2. QT程序的运行框架 2.4 设计界面元素的行为&#xff1a;信号、槽函数、对象之间的通信 2.4.1 对象之间的通信 2.4.2 信号槽与消息队里区别 2.4.3 预定义信号与默认槽函数 2.4.4 预定义信号与默认槽函数&#xff1a;案例2 2.4.5 预定义信号与自定义槽函数&#xff…

【JVM系列】类加载机制和双亲委派机制(一)

使用java8 一、类加载运行全过程 当我们用java命令运行某个类的main函数启动程序时&#xff0c;首先需要通过类加载器把主类加载到JVM。 package jvmdemo;public class Math {public static final int initData 666;public static User user new User();public int comput…

立创EDA(专业版)电路设计与制作快速入门

1.新建工程的时候 &#xff08;要注意用版本控制&#xff0c;项目名&#xff0c;日期&#xff0c;版本&#xff09; 2.原理图设计环境设置 3.电源转换电路 电源是一般是5v转3.3v电路 电源主要的可以选择LLDO来降压的或者是DCDC减压 这个电路主要是用LDO来降压的 AMS1117降…

操作系统Linux—day01

编程 网页 软件 网站项目 知识点&#xff1a; 前端页面&#xff1a;HTML css JavaScript JQuery VUE 数据库&#xff1a;Oracle MySQL 服务器&#xff1a;服务器的操作系统Linux Http://115.159.96.174:8090/EasyBuy/ www.baidu.com>http://180.101.49.12/ 后台技术…

【iOS】消息传递

Objective-C是一种动态语言&#xff0c;因此其很多行为是在运行时决定的。对于静态语言来说&#xff0c;函数的调用在编译时就已经确定。动态语言则不然&#xff0c;动态语言通过一些巧妙的机制使得函数的真实调用是在运行时决定的&#xff0c;即动态语言的特点是将一些决定性的…

2、常用基本命令-文件

Shell 可以看作是一个命令解释器&#xff0c;为我们提供了交互式的文本控制台界面。我们可以 通过终端控制台来输入命令&#xff0c;由 shell 进行解释并最终交给内核执行。 本章就将分类介绍 常用的基本 shell 命令。 7.1 帮助命令 7.1.1 man 获得帮助信息 1&#xff09;基本…

大于100万行数据如何转换坐标、格网生成和裁剪、低格网数据提取

在工作过程中&#xff0c;对于100万以下的数据&#xff0c;我们一般使用EXCEL进行操作&#xff0c;但对于大于1百万行的数据&#xff0c;EXCEL也无能为力。此时&#xff0c;解决办法有三种&#xff1a;&#xff08;1&#xff09;拆分成几个小于100万的文件&#xff0c;再用EXCE…

机器学习原理

此次主要是由分类来引出 由于宝可梦的图片比较简洁&#xff0c;而数码宝贝的图片就显得比较复杂&#xff0c;我们计算出他们的轮廓&#xff0c;求出白色数量的多少。 e的函数会计算线条的复杂程度。当得出的线条的复杂程度&#xff0c;小于h。我们认为其是宝可梦&#xff0c;反…

Python(七)二进制

❤️ 专栏简介&#xff1a;本专栏记录了我个人从零开始学习Python编程的过程。在这个专栏中&#xff0c;我将分享我在学习Python的过程中的学习笔记、学习路线以及各个知识点。 ☀️ 专栏适用人群 &#xff1a;本专栏适用于希望学习Python编程的初学者和有一定编程基础的人。无…