数据结构与算法|算法总结|滑动窗口篇

之前在用golang二刷代码随想录的时候,遇到209.长度最小的子数组竟然没想到应该用滑动窗口解题!怒而猛刷,并结合各个博客和视频总结滑动窗口题型和模板如下
参考资料:
【精心总结滑动窗口代码模板, 直接搞定80道Leetcode算法题】
【两分钟搞懂滑动窗口算法,致敬3b1b】
【labuladong算法笔记】

再次强调,主要是体会滑动窗口的核心思想,模板只是辅助。

文章目录

  • 滑动窗口的使用场景
  • 滑动窗口的核心思想
    • 使用思路(寻找最长)
    • 使用思路(寻找最短)
    • 模板
    • 典型题型推荐
      • 209. 长度最小的子数组
      • 3. 无重复字符的最长子串
        • set
        • map
        • 使用数组优化哈希表
      • 438. 找到字符串中所有字母异位词
        • 数组优化
      • 76. 最小覆盖子串

滑动窗口的使用场景

  • 很多题目都是找到所谓的子串、子数组、序列等等
  • 有一些要求:最长、最小、重复等等
  • 条件上的要求满足:串联、覆盖、有无重复、结算结果、出现次数、同时包含XX

一旦出现以上关键词,我们就应该考虑是否能考虑滑动窗口进行解答。

总而言之我认为,它往往是求一个连续的子序列,然后这个子序列要满足某种条件,这种题滑动窗口肯定是可以做的

滑动窗口的核心思想

滑动窗口一般有几个核心组件:

  • 左右指针构成一个窗口
    一般是首先移动右指针,然后判断当前窗内是否满足要求,满足要求存储结果,如果不满足要求了,就开始移动左指针,以求重新满足要求,当左右指针重合,可以认为是满足要求的特殊情况,重新开始移动右指针
  • 需要一个容器来存储结果
    一般是直接定义一个int整型数,在活动窗口中如果满足要求,将结果存储到容器中。

当右指针都到达末尾的时候,即整个流程结束

使用思路(寻找最长)

核心:左右双指针(L, R)在起始点,R向右逐位滑动
每次滑动过程中:
if : 窗口内元素满足条件,R向右扩大窗口,并更新最优结果
if : 窗口内元素不满足条件,L向右缩小窗口
流程结束:R指针到达结尾,整个流程结束

使用思路(寻找最短)

核心:左右双指针(L, R)在起始点,R向右逐位滑动
每次滑动过程中:
if : 窗口内元素满足条件,L向右缩小窗口,并更新最优结果
if : 窗口内元素不满足条件,R向右扩大窗口
流程结束:R指针到达结尾,整个流程结束

模板

两种情况在代码实现中最大的区别就是:内循环中,一个是result不满足要求;一个是当result满足要求!
最长(大)模板

初始化left, right, result, bestResult
某些时候可能需要合适的容器来承载result和bestResult
个人觉得最常用的就是哈希(包括数组、map、set)
while (右指针没有到结尾) {窗口扩大,加入right对应元素,更新当前resultwhile/if (result不满足要求) {窗口缩小,移除left对应元素,left右移}更新最优结果bestResultright++
}
返回bestResult;

这里while/if的区别主要在于如何移动指针,是逐步缩小窗口,还是直接开始新的窗口更新。
最短(小)模板

初始化left, right, result, bestResult
某些时候可能需要合适的容器来承载result和bestResult
个人觉得最常用的就是哈希(包括数组、map、set)
while (右指针没有到结尾) {窗口被扩大,加入right对应元素,更新当前resultwhile/if (result满足要求) {窗口缩小,移除left对应元素,left右移}更新最优结果bestResultright++
}
返回bestResult;

典型题型推荐

以下为最经典的题型:来自代码随想录和LeeCode hot100

  • 209. 长度最小的子数组
  • 3. 无重复字符的最长子串
  • 438. 找到字符串中所有字母异位词
  • 76. 最小覆盖子串

209. 长度最小的子数组

class Solution {
public:int minSubArrayLen(int target, vector<int>& nums) {int right = 0, left = 0;int curSum = 0;int bestResult = INT_MAX;  // 使用INT_MAX来表示无效的大数,方便之后取最小值while (right < nums.size()) {curSum += nums[right];  // 扩展窗口右边界while (curSum >= target) {bestResult = min(bestResult, right - left + 1);  // 更新最短长度curSum -= nums[left];  // 缩小窗口left++;  // 正确的是left增加}right++;  // 继续向右扩展窗口}return bestResult == INT_MAX ? 0 : bestResult;  // 如果没有更新过bestResult,返回0}
};

3. 无重复字符的最长子串

本题最核心的就是选一个容器来装字符:

set
class Solution {
public:int lengthOfLongestSubstring(string s) {unordered_set<char> set;  // 用于存储窗口中的字符int left = 0, right = 0;  // 双指针,表示当前的滑动窗口[left, right)int bestResult = 0;       // 最长无重复字符的子串长度while (right < s.length()) {char rChar = s[right];  // 右指针对应的字符// 如果字符已经存在于set中,移动左指针直到移除重复字符while (set.find(rChar) != set.end()) {set.erase(s[left]);left++;}// 添加新字符到set中,更新结果,移动右指针set.insert(rChar);bestResult = max(bestResult, right - left + 1);right++;}return bestResult;}
};
map

第一反应是使用unordered_map,我们可以在map中key为出现的字符,value为该字符的下标,方便左边的更新,下边的数组优化方案也是一样的道理,所以并这类题并不需要循环

class Solution {
public:int lengthOfLongestSubstring(string s) {unordered_map<char, int> map;  // 字符到其最近出现位置的映射int left = 0, right = 0;       // 双指针,表示当前的滑动窗口[left, right)int result = 0;                // 当前窗口的长度int bestResult = 0;            // 最长无重复字符的子串长度while (right < s.length()) {char rChar = s[right];     // 右指针对应的字符if (map.find(rChar) != map.end()) {// 如果字符已经存在,则可能需要移动左指针left = max(left, map[rChar] + 1);}map[rChar] = right;        // 更新或添加字符的最新索引result = right - left + 1; // 更新当前窗口的长度bestResult = max(bestResult, result); // 更新最长子串的长度right++;                   // 移动右指针}return bestResult;}
};
使用数组优化哈希表
int lengthOfLongestSubstring(string s) {vector<int> index(256, -1);  // ASCII 字符集,所有元素初始化为 -1int left = 0, right = 0;int bestResult = 0;while (right < s.length()) {char rChar = s[right];// 如果当前字符已出现过且索引大于等于左指针,则更新左指针if (index[rChar] != -1 && index[rChar] >= left) left = index[rChar] + 1;// 更新当前字符的索引index[rChar] = right;// 计算当前无重复字符子串的长度,并更新最长长度bestResult = max(bestResult, right - left + 1);// 移动右指针right++;}return bestResult;
}

438. 找到字符串中所有字母异位词

拿到本题很直观的感觉,我们需要一个容器装p,记录他出现的字母和次数,然后需要一个容器装我们滑动窗口中的字符,然后这两个容器如果匹配上了,那肯定就是异构词了。

class Solution {
public:vector<int> findAnagrams(string s, string p) {vector<int> result;if (s.size() < p.size()) return result;unordered_map<char, int> pCount, sCount;//初始化p的字符频率表for (char c : p) {pCount[c]++;}int left = 0, right = 0;int required = p.size();while (right < s.size()) {//加入当前右指针char rChar = s[right];sCount[rChar]++;//当窗口大小匹配P长度时进行比较if (right - left + 1 == required) {if (sCount == pCount) { //存结果result.push_back(left);}//否则窗口左端向右移动,缩小窗口sCount[s[left]]--;if (sCount[s[left]] == 0) {sCount.erase(s[left]);}left++;}right++;}return result;}
};
数组优化
  • 我们使用固定大小的数组代替哈希表
  • 减少不必要的比较
    • 我们在之前的实现,每次窗口大小达到p长度时,我们都会比较两个哈希表,我们可以只在字符频率匹配时才进行这样的比较,即当插入或删除操作可能改变频率表到匹配状态时才检查
  • 滑动窗口计数
    • 维护一个计数器来跟踪已匹配的字符种类数量。例如,当某个字符的期望频率与窗口中的频率相匹配时,增加计数器。如果所有字符都匹配,计数器将等于不同字符的总数。这样可以在不比较整个哈希表(指数组)的情况下,通过检查计数器来判断当前窗口是否为有效的异位词。
class Solution {
public:vector<int> findAnagrams(string s, string p) {vector<int> result;if (s.size() < p.size()) return result;vector<int> pCount(256, 0), sCount(256, 0);for (char c : p) {pCount[c]++;}int left = 0, right = 0, count = 0;int pLength = p.size();while (right < s.size()) {// 增加右指针字符if (pCount[s[right]] > 0 && ++sCount[s[right]] <= pCount[s[right]]) {count++;}// 当窗口大小正确,并且计数匹配if (right - left + 1 == pLength) {if (count == pLength) {result.push_back(left);}// 减少左指针字符if (pCount[s[left]] > 0 && sCount[s[left]]-- <= pCount[s[left]]) {count--;}left++;}right++;}return result;}
};

76. 最小覆盖子串

和上一题几乎一样

class Solution {
public:string minWindow(string s, string t) {if (s.empty() || t.empty() || s.size() < t.size()) return "";// 字符计数数组unordered_map<char, int> need, have;for (char c : t) {need[c]++;}// required 表示需要涵盖的字符种类数int required = need.size();int left = 0, right = 0, formed = 0;int minLen = INT_MAX, minStart = 0; // 用于记录最小子串的起始位置和长度while (right < s.length()) {char c = s[right];have[c]++;// 如果当前字符的数量符合需求的数量,增加formedif (need.count(c) && have[c] == need[c]) {formed++;}// 尝试缩小窗口,直到窗口不再满足条件while (left <= right && formed == required) {char temp = s[left];// 更新最小窗口if (right - left + 1 < minLen) {minLen = right - left + 1;minStart = left;}// 移动左指针,更新have数组和formed计数have[temp]--;if (need.count(temp) && have[temp] < need[temp]) {formed--;}left++;}// 移动右指针right++;}return minLen == INT_MAX ? "" : s.substr(minStart, minLen);}
};

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

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

相关文章

根据token获取了username后,能否在其他地方使用这个获取的username,或者在其他地方如何获取username?

当然可以在其他地方使用获取到的用户名。一旦你从token中获取到用户名&#xff0c;你可以将其存储在能够在整个应用程序中访问的地方。 在你的代码中&#xff0c;你从token中获取用户名的地方是这里&#xff1a; String username getUsernameFromToken(token);在这行之后&am…

Windows平台PyCharm之PySide6开发环境搭建与配置

PySide6 是一个用于创建跨平台 GUI 应用程序的库&#xff0c;它是 Qt for Python 的官方库。Qt 是一个跨平台的 C 应用程序框架&#xff0c;用于开发具有图形用户界面&#xff08;GUI&#xff09;的应用程序。PySide6 允许开发者使用 Python 语言访问 Qt 的功能&#xff0c;从而…

数据结构(十一)----图的应用

目录 一.最小生成树 1.Prim算法&#xff08;普里姆&#xff09; 2.Kruskal算法(克鲁斯卡尔): 二.最短路径&#xff08;BFS算法&#xff09; 1.单源最短路径 &#xff08;1&#xff09;BFS算法&#xff08;无权图&#xff09; &#xff08;2&#xff09;Dijkstra算法&…

RuoYi-Vue-Plus (SpringCache、CacheManager、@Cacheable)

一、概述 1、SpringCache是Spring提供的一个缓存框架&#xff0c;在Spring3.1版本开始支持将缓存添加到现有的spring应用程序中&#xff0c;在4.1开始&#xff0c;缓存已支持JSR-107注释和更多自定义的选项。 2、SpringCache利用了AOP&#xff0c;实现了基于注解的缓存功能&…

不走寻常路!酷开科技不断升级酷开系统满足消费者日益增长的需求

在科技日新月异的今天&#xff0c;人们对生活品质的要求越来越高。为此&#xff0c;酷开科技不断升级酷开系统&#xff0c;以满足消费者日益增长的需求。为了让消费者体验更好的服务&#xff0c;在酷开系统中设立了酷开会员&#xff0c;满足消费者的更多需求。丰富的特权和定制…

STL学习笔记

1 基本概念 1.1 STL STL(Standard Template Library,标准模板库)STL从广义上分为: 容器(container) 算法(algorithm) 选代器(iterator)容器和算法之间通过迭代器&#xff08;看作指针&#xff09;进行无缝连接STL 几乎所有的代码都采用了横板类或者模板函数 1.2 容器 STL容器…

图片如何压缩到500kb以下?3步完成图片压缩

在日常生活和工作中&#xff0c;经常需要处理各种图片&#xff0c;而有时候图片文件过大&#xff0c;不仅占用了大量的存储空间&#xff0c;还可能影响文件的传输速度和加载速度。因此&#xff0c;如何将图片压缩到500kb以内成为了许多人的需求&#xff0c;普通的图片压缩可能没…

使用Docker安装Whistle Web Debugging Proxy

大家好&#xff0c;继续给大家分享如何使用docker来安装Whistle Web Debugging Proxy&#xff0c;关于Whistle Web Debugging Proxy的介绍和使用&#xff0c;大家可以参考下面文章&#xff0c;希望本文能够给大家的工作带来一定帮助。 Whistle Web Debugging Proxy介绍及使用 …

vue+lodop实现web端打印标签功能

背景&#xff1a;项目要求在web端连接标签打印机&#xff0c;打印收件人信息 lodop打印插件地址&#xff1a;Lodop和C-Lodop官网主站 在项目中使用 1、去官网下载lodop包下载中心 - Lodop和C-Lodop官网主站 windows系统直接下载windows32版的就可以 2、解压安装 点击CLodop…

SpringCloud Config 分布式配置中心

SpringCloud Config 分布式配置中心 概述分布式系统面临的——配置问题ConfigServer的作用 Config服务端配置Config客户端配置 可以有一个非常轻量级的集中式管理来协调这些服务 概述 分布式系统面临的——配置问题 微服务意味着要将单体应用中的业务拆分成一个个字服务&…

linux不同引号的含义(随手记)

单引号&#xff1a; 所见即所得,单引号里面的内容会原封不动输出. echo test--hostname--$(hostname)--{1..5} test--hostname--$(hostname)--{1..5}双引号&#xff1a; 和单引号类似,对双引号里面的特殊符号会进行解析,对于{}花括号(通配符)没有解析. echo "test--host…

python如何整体缩进

python自带编辑器的缩进和取消缩进快捷键&#xff1a; 整体缩进 Ctrl【 整体取消缩进 Ctrl】 pycharm编辑器的缩进和取消缩进快捷键&#xff1a; 整体缩进&#xff1a; tab 整体取消缩进&#xff1a; tabshift

HDMI ARC功能详解及应用介绍

一、HDMI HDMI(High-Definition Multimedia Interface&#xff0c;高清多媒体接口)&#xff0c;是一种专用的音频/视频接口&#xff0c;用于发送未压缩的视频数据和压缩/未压缩的音频数据。HDMI是模拟视频标准的数字替代品。HDMI视频和音频信号传输通道采用了TMDS&#xff08;T…

【经验总结】Vue2中的全局变量(store

需求场景 需要在vue中存储一个可变的&#xff0c;可读写的全局变量在不同的js、页面中均可调用和读写 技术&#xff1a;使用vue的store 用法总结 一、定义变量 1、找到vue的/src/store路径&#xff0c;在modules文件夹下创建文件&#xff08;这里便于测试创建demo.js&…

51单片机入门:DS1302时钟

51单片机内部含有晶振&#xff0c;可以实现定时/计数功能。但是其缺点有&#xff1a;精度往往不高、不能掉电使用等。 我们可以通过DS1302时钟芯片来解决以上的缺点。 DS1302时钟芯片 功能&#xff1a;DS1302是一种低功耗实时时钟芯片&#xff0c;内部有自动的计时功能&#x…

十二届蓝桥杯Python组3月中/高级试题 第二题

** 十二届蓝桥杯Python组3月中/高级试题 第二题 ** 第二题&#xff08;难度系数 3&#xff0c;20 个计分点&#xff09; 编程实现&#xff1a; 给定一个正整数&#xff0c;判断这个正整数是否能被5整除。 输入描述&#xff1a;输入一个正整数n 输出描述&#xff1a;如果n可以…

SpringBoot启动流程源码解析

目录 一、SpringApplication构造方法解析 1. web应用类型 2. BootstrapRegistryInitializer 3. ApplicationContextInitializer 4. ApplicationListener 5. 推断Main方法所在类 二、SpringApplication.run(String... args)方法解析 1.创建DefaultBootstrapContext 2.获…

订单超时自动取消的实践方案

1、定时任务方案 方案流程&#xff1a; 每隔 30 秒查询数据库&#xff0c;取出最近的 N 条未支付的订单。 遍历查询出来的订单列表&#xff0c;判断当前时间减去订单的创建时间是否超过了支付超时时间&#xff0c;如果超时则对该订单执行取消操作。 定时任务方案工程实现相…

基于AC-YOLO的路面落叶检测方法

基于AC-YOLO的路面落叶检测方法 A Road Leaf Detection Method based on AC-YOLO 完整下载链接:基于AC-YOLO的路面落叶检测方法 文章目录 基于AC-YOLO的路面落叶检测方法摘要第一章 引言1.1 研究背景1.2 研究意义1.3 相关工作 第二章 AC-YOLO算法介绍2.1 目标检测技术综述2.2…

【Vue】vue中将 html 或者 md 导出为 word 文档

原博主 xh-htmlword文档 感谢这位大佬的封装优化和分享&#xff0c;亲测有用&#xff01;可以去看大佬&#x1f447;的说明&#xff01; 前端HTML转word文档&#xff0c;绝对有效&#xff01;&#xff01;&#xff01; 安装 npm install xh-htmlword导入 import handleEx…