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

滑动窗口大总结!!!妈妈以后再也不担心我不会做滑动窗口啦~

写在前面:全部题都源于力扣

  • 讲解
  • 题目一:最小覆盖子串
  • 题目二:字符串排列
  • 题目三:找所有字母异位词
  • 题目四:无重复字符的最长子串
  • 题目五:滑动窗口的最大值

讲解

滑动窗口算法技巧主要用来解决子数组问题,比如让你寻找符合某个条件的最长/最短子数组。
如果用暴力解的话,你需要嵌套 for 循环这样穷举所有子数组,时间复杂度是O(n2)

for(int i = 0; i < nums.size(); i ++){for(int j = i; j < nums.size(); j ++){//维护一个nums[i,j]的子数组}
}

滑动窗口其实也并不难就是维护一个窗口,不断滑动,不断更新答案,大致逻辑:

int left = 0, right = 0;while (right < nums.size()) {// 增大窗口window.addLast(nums[right]);right++;while (window needs shrink) {// 缩小窗口window.removeFirst(nums[left]);left++;}
}

由于这套逻辑left和right都不会回退,所以滑动窗口的时间复杂度是O(n)

没了,讲解到此结束在这里插入图片描述
只学讲解是没有办法乱杀滴,接下来就靠着这个模板魔改解决hard题吧!

题目一:最小覆盖子串

力扣难度hard->传送门
在这里插入图片描述
滑动窗口的思路是这样的:

  1. 在字符串 S 中使用双指针中的左右指针技巧,初始化 left = right = 0,把索引左闭右开区间 [left, right) 称为一个「窗口」。

问:为什么设计为左闭右开区间?
答:因为这样初始化 left = right = 0 时区间 [0, 0) 中没有元素,但只要让 right 向右移动(扩大)一位,区间 [0, 1) 就包含一个元素 0 了。如果设置为两端都开的区间,那么让 right 向右移动一位后开区间 (0,1) 仍然没有元素;如果设置为两端都闭的区间,那么初始区间 [0, 0] 就包含了一个元素。这两种情况都会给边界处理带来不必要的麻烦。

  1. 先不断地增加 right 指针扩大窗口 [left, right),直到窗口中的字符串符合要求(包含了 t 中的所有字符)。
  2. 此时,我们停止增加 right,转而不断增加 left 指针缩小窗口 [left, right),直到窗口中的字符串不再符合要求(不包含 t 中的所有字符了)。同时,每次增加 left,我们都要更新一轮结果
  3. 重复第 2 和第 3 步,直到 right 到达字符串 s 的尽头。

talk is cheap,show me the code!

首先,需要window和need两个哈希表,用来记录窗口中已有的字符和需要凑齐的字符

// 记录 window 中的字符出现次数
unordered_map<char, int> window;
// 记录所需的字符出现次数
unordered_map<char, int> need;
for(char c : t) need[c] ++;

现在开始套模板,只需要思考以下几个问题:

  1. 什么时候应该移动 right 扩大窗口?窗口加入字符时,应该更新哪些数据?
    答:只要窗口内没有满足t字符都有的话就应该继续扩大窗口,窗口加入字符时,需要更新窗口大小(window++),必备字符个数(window[c]++),已满条件的字符数(valid++)。
while(right < s.size()){char c = s[right];//加入滑动窗口的值right ++;//窗口变大//新加入的值是否需要,需要的话:if(need(c)){window[c]++;//已有的必备值加加if(window[c] == need[c]) valid++;//如果某个字符在此窗口已经满足条件,valid++}
}
  1. 什么时候窗口应该暂停扩大,开始移动 left 缩小窗口?从窗口移出字符时,应该更新哪些数据?
    答:当 valid 满足 need 时应该收缩窗口,应该在收紧窗口的时候更新最终数据,更新窗口大小,更新valid(移除元素了,这里只可能减),window[字符]数量,另外更新最小覆盖子串的起始位置。因为答案一定是在缩窗口的时候出现的,所以应该在这里更新len和start
            // 判断左侧窗口是否要收缩while (valid == need.size()) {// 在这里更新最小覆盖子串if (right - left < len) {start = left;len = right - left;}// d 是将移出窗口的字符char d = s[left];// 缩小窗口left++;// 进行窗口内数据的一系列更新if (need.count(d)) {if (window[d] == need[d])valid--;window[d]--;}
  1. 我们要的结果应该在扩大窗口时还是缩小窗口时进行更新?
    答:一定是在缩小的时候

整体代码:

class Solution {
public:string minWindow(string s, string t) {unordered_map<char, int> need, window;for (char c : t) {need[c]++;}int left = 0, right = 0;// 记录window中的字符满足need条件的字符个数int valid = 0;// 记录最小覆盖子串的起始索引及长度int start = 0, len = INT_MAX;while (right < s.size()) {// c 是将移入窗口的字符char c = s[right];// 扩大窗口right++;// 进行窗口内数据的一系列更新if (need.count(c)) {window[c]++;if (window[c] == need[c])valid++;}// 判断左侧窗口是否要收缩// 用while!!一种缩到不能再缩while (valid == need.size()) {// 在这里更新最小覆盖子串// 必须先将len记录下来再更新窗口大小// 只有这样才能记录每一次合法len,然后更新if (right - left < len) {start = left;len = right - left;}// d 是将移出窗口的字符char d = s[left];// 缩小窗口left++;// 进行窗口内数据的一系列更新if (need.count(d)) {if (window[d] == need[d])valid--;window[d]--;}}}// 返回最小覆盖子串// 等于INT_MAX的话返回的是""不是" "return len == INT_MAX ? "" : s.substr(start, len);}
};

题目二:字符串排列

力扣567
在这里插入图片描述
mid难度,s1是可以包含重复字符的哦

是明显的滑动窗口算法,相当给你一个 S 和一个 T,请问你 S 中是否存在一个子串,包含 T 中所有字符且不包含其他字符?

基本和题目一是一样的,只有几个地方需要注意:

  1. 本题移动 left 缩小窗口的时机是窗口大小大于 t.length() 时,因为排列嘛,显然长度应该是一样的。
  2. 当发现 valid == need.size() 时,就说明窗口中就是一个合法的排列,所以立即返回 true。至于如何处理窗口的扩大和缩小,和最小覆盖子串完全相同。

完整代码:

class Solution {
public:bool checkInclusion(string t, string s) {unordered_map<char, int> need, window;for (char c : t) need[c]++;int left = 0, right = 0, valid = 0;while (right < s.size()) {char c = s[right++];if (need.count(c)) {window[c]++;if (window[c] == need[c]) valid++;}while (right - left > t.size()) { // 严格大于,以便准确控制窗口大小char d = s[left++];if (need.count(d)) {if (window[d] == need[d]) valid--;window[d]--;}}// valid == need.size()!!!if (right - left == t.size() && valid == need.size()) // 确保窗口大小严格等于t的长度return true;}return false;}
};

题目三:找所有字母异位词

力扣438
在这里插入图片描述
异位词,就是排列啊,搞了个高端的说法,也糊弄不了我们这些绝顶聪明的娃在这里插入图片描述
直接上代码

class Solution {
public:vector<int> findAnagrams(string s, string t) {unordered_map<char, int> need, window;for (char c : t) {need[c]++;}int left = 0, right = 0;int valid = 0;// 记录结果vector<int> res;while (right < s.size()) {char c = s[right];right++;// 进行窗口内数据的一系列更新if (need.count(c)) {window[c]++;if (window[c] == need[c]) {valid++;}}// 判断左侧窗口是否要收缩while (right - left > t.size()) {char d = s[left];left++;// 进行窗口内数据的一系列更新if (need.count(d)) {if (window[d] == need[d]) {valid--;}window[d]--;}}if(right - left == t.size() && valid == need.size()){res.push_back(left);}}return res;}
};

题目四:无重复字符的最长子串

力扣3.
这题在双指针里面用双指针解决过了
如果用滑动窗口的话也很容易
窗口缩的条件就是window[c] > 1,说明有重复了
和双指针思路一模一样
双指针:维护[j,i]数组

#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int a[N];
int s[N];
int main(){int n;int r = 0;cin >> n;for(int i = 0, j = 0; i < n; i ++){cin >> a[i];s[a[i]] ++;//记录个数while(s[a[i]] > 1){-- s[a[j ++]];}r = max(r, i - j + 1);}cout << r;
}

滑动窗口:

class Solution {
public:int lengthOfLongestSubstring(string s) {int left = 0;int right = 0;int r = 0;unordered_map<char, int> window;while(right < s.size()){char c = s[right];right++;window[c]++;while(window[c] > 1){char d = s[left];window[d]--;left++;}r = max(r, right - left);}return r;}
};

题目五:滑动窗口的最大值

经典滑动窗口,力扣239
又名单调队列的实现
在这里插入图片描述

和题目1~4不一样,这题滑动窗口大小固定,每一次的移动也固定,难点在于求最大值

这里实现队列,要有pop,push操作,因为题目的特殊性,再加个返回最大值的max操作
我们需要逐步实现这三个API

push:
push 方法依然在队尾添加元素,但是要把前面比自己小的元素都删掉
在这里插入图片描述

void push(int n) {// 将前面小于自己的元素都删除while (!maxq.empty() && maxq.back() < n) {maxq.pop_back();}maxq.push_back(n);
}

max:
如果每个元素被加入时都这样操作,最终单调队列中的元素大小就会保持一个单调递减的顺序,因此我们的 max 方法可以可以这样写:

int max() {// 队头的元素肯定是最大的return maxq.front();}

pop:
pop 方法在队头删除元素 n:

void pop(int n) {if (n == maxq.front()) {maxq.pop_front();}
}

所以综合代码:

class slidingQueue{
private:deque<int> maxq;
public:void push(int n){while(!maxq.empty() && n > maxq.back()) maxq.pop_back();maxq.push_back(n);}int max(){return maxq.front();}void pop(int n){if(!maxq.empty() && maxq.front() == n) maxq.pop_front();}
};
class Solution {
public:vector<int> maxSlidingWindow(vector<int>& nums, int k) {slidingQueue window;vector<int> res;for(int i = 0; i < nums.size(); i ++){if(i < k - 1){window.push(nums[i]);}else{window.push(nums[i]);res.push_back(window.max());window.pop(nums[i - k + 1]);}}return res;}
};

相关文章:

滑动窗口大总结!!!妈妈以后再也不担心我不会做滑动窗口啦~

写在前面&#xff1a;全部题都源于力扣 讲解题目一&#xff1a;最小覆盖子串题目二&#xff1a;字符串排列题目三&#xff1a;找所有字母异位词题目四&#xff1a;无重复字符的最长子串题目五&#xff1a;滑动窗口的最大值 讲解 滑动窗口算法技巧主要用来解决子数组问题&#…...

从地铁客流讲开来:客流统计与清分释义

一、常见的客流统计 1. 进站客流 定义&#xff1a;指在某个时间段内&#xff0c;乘客进入地铁站的数量。示例&#xff1a;如果某天早上8点到9点之间有5000人次进入地铁站&#xff0c;则这段时间内的进站客流为5000人次。 2. 出站客流 定义&#xff1a;指在某个时间段内&…...

《Excelize权威指南》新书发布

在数据洪流涌动的数字化时代&#xff0c;数据处理与分析已跃升为解锁无限洞察力的金钥匙&#xff0c;赋能商业智慧、重塑医疗健康版图、驱动教育科研创新。然而&#xff0c;当数据量级爆炸式增长&#xff0c;传统工具如 Excel 虽被誉为数据处理领域的常青树&#xff0c;其手动操…...

Go语言加Vue3零基础入门全栈班11 Go语言+gorm用户管理系统实战 2024年08月03日 课程笔记

概述 如果您没有Golang的基础&#xff0c;应该学习如下前置课程。 Golang零基础入门Golang面向对象编程Go Web 基础Go语言开发REST API接口_20240728Go语言操作MySQL开发用户管理系统API教程_20240729Redis零基础快速入门_20231227GoRedis开发用户管理系统API实战_20240730Mo…...

【设计模式】代理模式详解

1.简介 代理模式是常用的Java设计模式&#xff0c;该模式的特点是代理类与委托类共享相同的接口。代理类主要负责预处理消息、过滤消息、将消息转发给委托类&#xff0c;并在事后处理消息等。代理类与委托类之间通常存在关联关系&#xff0c;一个代理类对象与一个委托类对象关…...

Python变量和简单的数据类型

1、变量 massageHello python world! print(massage) massageHello world print(massage) 运行这个代码发现&#xff0c;同一个变量出现两个不同的结果 Hello python world! Hello world 在程序中&#xff0c;可随时修改变量的值&…...

切比雪夫距离

切比雪夫距离&#xff08;Chebyshev Distance&#xff09;&#xff0c;又称棋盘距离或最大值距离&#xff0c;是一种用于测量两个点之间距离的度量方法。在二维平面上&#xff0c;切比雪夫距离定义为两个点之间的最大坐标差值。其公式如下&#xff1a; DChebyshevmax⁡(∣x2−…...

计算机基础(Windows 10+Office 2016)教程 —— 第4章 计算机网络与Internet(下)

第4章 计算机网络与Internet 4.4 局域网4.4.1 局域网概述4.4.2 以太网4.4.3 令牌环网4.4.4 无线局域网 4.5 Internet4.5.1 Internet 概述4.5.2 Internet 的基本概念4.5.3 Internet 的接入4.5.4 万维网 4.6 Internet的应用4.6.1 电子邮件4.6.2 文件传输4.6.3 搜索引擎 4.4 局域网…...

机器学习用Python还是R?哪个更好一些?

选择使用Python还是R来进行机器学习取决于多个因素&#xff0c;包括个人偏好、项目需求以及可用的资源。这里我可以简要比较一下它们的优缺点&#xff1a; Python的优势&#xff1a; 通用性和灵活性&#xff1a; Python是一种通用编程语言&#xff0c;可以用于多种用途&#…...

4个自定义倒计时

<!DOCTYPE html> <html lang"zh-CN"><head><meta charset"UTF-8"><title>4个自定义倒计时</title><style>* {margin: 0;padding: 0;box-sizing: border-box;user-select: none;body {background: #0b1b2c;}}hea…...

linux系统编程中Shell脚本配置,及linux脚本中的man test

Shell脚本配置是指在脚本中设置各种参数、选项和环境&#xff0c;以确保脚本能够根据预期的需求和环境执行。配置可以包括变量设置、环境变量、命令选项和错误处理等。 1. 脚本开头的配置 Shebang 第一行通常是shebang&#xff0c;它告诉系统使用哪个解释器来执行脚本。例如…...

Win7虚拟机分享(已安装VMware Tools)

前言 之前写过VMware安装Win7并安装VMware tools的博客&#xff0c;但操作仍显繁琐。后来发现可以直接分享已经配置好的虚拟机&#xff0c;所有软件都是安装好的&#xff0c;解压即用。 一. VMware Win7虚拟机配置 已完成的配置和安装的软件 专业版Win7系统(已永久激活)VMware…...

CANOpen EMCY紧急报文介绍

什么是CANOpen紧急报文 CANOpen中的Emcy紧急报文用于当设备出现故障或警告时&#xff0c;向其它节点报告故障或警告使用的。如设备某个设备出现过压或过流时&#xff0c;就可以发送紧急报文。 紧急报文的格式 错误代码&#xff1a;是0x1003索引预定义错误字段的内容&#xff…...

JAVA项目

目录 一、前言 二、技术介绍 三、项目实现流程 四、论文流程参考 五、核心代码截图 专注于大学生实战开发、讲解和毕业答疑等辅导&#xff0c;获取源码后台 一、前言 在数字化音乐时代&#xff0c;个性化推荐已成为提升用户体验、促进音乐消费的重要手段。为此&#xff0…...

️ LangChain +Streamlit+ Llama :将对话式人工智能引入您的本地设备(下篇)

引言&#xff1a;种下一棵树最好的时间是十年前,其次是现在 书接上回&#xff1a;将对话式人工智能引入您的本地设备成为可能CSDNhttps://mp.csdn.net/mp_blog/creation/editor/140865426 目的&#xff1a;在这个大模型横行的时候&#xff0c;我们常用电脑如何开展大模型的工作…...

Kafka实战(Scala操作)

Kafka基础讲解部分 Kafka基础讲解部分 Kafka实战&#xff08;Scala操作&#xff09; 1、引入依赖 版本&#xff1a; <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.report…...

Android Framework 之WMS详解

1.WMS说的就是 WindowManagerService&#xff1a;负责为Activity对应的窗口分配Surface&#xff0c;管理Surface的显示顺序以及位置尺寸&#xff0c;控制窗口动画 。 它是Android系统中为各个客户端即每个app来提供这样的服务的一个类。 在Android系统中在systemServer 进程和各…...

opencv-图像仿射变换

仿射变换设计图像位置角度的变化&#xff0c;是深度学习预处理中常用的功能。仿射变换就是对图像的平移缩放旋转翻转操作的组合 如下图&#xff0c;对图中点1,2,3与图二中三个点一一映射&#xff0c;仍然形成三角形&#xff0c;但形状已经发生改变&#xff0c;通过这两组三点求…...

算法的基本概念

一、算法的基本概念思维导图 二、什么是算法&#xff1a; 1.我们知道数据结构就是将我门现实的世界中的问题数据化&#xff0c;存入计算机中&#xff0c;并实现对数据结构的一些基本操作。 2.算法就是如何处理这些存入计算机中的信息&#xff0c;以求高效的解决实际问题。 3…...

124. Go Template应用实例:用代码生成代码

文章目录 生成器模式生成器代码生成 本文用生成器模式作为例子&#xff0c;来演示如何用代码生成代码。 生成器模式 熟悉 Java 开发的同学都知道&#xff0c;lombok 有一个著名的注解 Builder &#xff0c;只要加在类上面&#xff0c;就可以自动生成 Builder 模式的代码。如下…...

synchronized 学习

学习源&#xff1a; https://www.bilibili.com/video/BV1aJ411V763?spm_id_from333.788.videopod.episodes&vd_source32e1c41a9370911ab06d12fbc36c4ebc 1.应用场景 不超卖&#xff0c;也要考虑性能问题&#xff08;场景&#xff09; 2.常见面试问题&#xff1a; sync出…...

Appium+python自动化(十六)- ADB命令

简介 Android 调试桥(adb)是多种用途的工具&#xff0c;该工具可以帮助你你管理设备或模拟器 的状态。 adb ( Android Debug Bridge)是一个通用命令行工具&#xff0c;其允许您与模拟器实例或连接的 Android 设备进行通信。它可为各种设备操作提供便利&#xff0c;如安装和调试…...

Auto-Coder使用GPT-4o完成:在用TabPFN这个模型构建一个预测未来3天涨跌的分类任务

通过akshare库&#xff0c;获取股票数据&#xff0c;并生成TabPFN这个模型 可以识别、处理的格式&#xff0c;写一个完整的预处理示例&#xff0c;并构建一个预测未来 3 天股价涨跌的分类任务 用TabPFN这个模型构建一个预测未来 3 天股价涨跌的分类任务&#xff0c;进行预测并输…...

unix/linux,sudo,其发展历程详细时间线、由来、历史背景

sudo 的诞生和演化,本身就是一部 Unix/Linux 系统管理哲学变迁的微缩史。来,让我们拨开时间的迷雾,一同探寻 sudo 那波澜壮阔(也颇为实用主义)的发展历程。 历史背景:su的时代与困境 ( 20 世纪 70 年代 - 80 年代初) 在 sudo 出现之前,Unix 系统管理员和需要特权操作的…...

Matlab | matlab常用命令总结

常用命令 一、 基础操作与环境二、 矩阵与数组操作(核心)三、 绘图与可视化四、 编程与控制流五、 符号计算 (Symbolic Math Toolbox)六、 文件与数据 I/O七、 常用函数类别重要提示这是一份 MATLAB 常用命令和功能的总结,涵盖了基础操作、矩阵运算、绘图、编程和文件处理等…...

【C语言练习】080. 使用C语言实现简单的数据库操作

080. 使用C语言实现简单的数据库操作 080. 使用C语言实现简单的数据库操作使用原生APIODBC接口第三方库ORM框架文件模拟1. 安装SQLite2. 示例代码:使用SQLite创建数据库、表和插入数据3. 编译和运行4. 示例运行输出:5. 注意事项6. 总结080. 使用C语言实现简单的数据库操作 在…...

3403. 从盒子中找出字典序最大的字符串 I

3403. 从盒子中找出字典序最大的字符串 I 题目链接&#xff1a;3403. 从盒子中找出字典序最大的字符串 I 代码如下&#xff1a; class Solution { public:string answerString(string word, int numFriends) {if (numFriends 1) {return word;}string res;for (int i 0;i &…...

IoT/HCIP实验-3/LiteOS操作系统内核实验(任务、内存、信号量、CMSIS..)

文章目录 概述HelloWorld 工程C/C配置编译器主配置Makefile脚本烧录器主配置运行结果程序调用栈 任务管理实验实验结果osal 系统适配层osal_task_create 其他实验实验源码内存管理实验互斥锁实验信号量实验 CMISIS接口实验还是得JlINKCMSIS 简介LiteOS->CMSIS任务间消息交互…...

Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信

文章目录 Linux C语言网络编程详细入门教程&#xff1a;如何一步步实现TCP服务端与客户端通信前言一、网络通信基础概念二、服务端与客户端的完整流程图解三、每一步的详细讲解和代码示例1. 创建Socket&#xff08;服务端和客户端都要&#xff09;2. 绑定本地地址和端口&#x…...

基于TurtleBot3在Gazebo地图实现机器人远程控制

1. TurtleBot3环境配置 # 下载TurtleBot3核心包 mkdir -p ~/catkin_ws/src cd ~/catkin_ws/src git clone -b noetic-devel https://github.com/ROBOTIS-GIT/turtlebot3.git git clone -b noetic https://github.com/ROBOTIS-GIT/turtlebot3_msgs.git git clone -b noetic-dev…...