算法中的移动窗帘——C++滑动窗口算法详解
1. 滑动窗口简介
滑动窗口是一种在算法中常用的技巧,主要用来处理具有连续性的子数组或子序列问题。通过滑动窗口,可以在一维数组或字符串上维护一个固定或可变长度的窗口,逐步移动窗口,避免重复计算,从而提升效率。常用于求解子数组的最大/最小值、满足条件的子数组个数等问题。
滑动窗口的两种类型
1. 固定大小的滑动窗口:窗口大小固定不变,通常适合于问题中要求找定长子数组的最大值、最小值等情况。
2. 可变大小的滑动窗口:窗口大小根据条件动态调整,通常适用于子数组和达到一定值或满足特定条件的情况。
滑动窗口的实现步骤
以可变大小的滑动窗口为例,一般步骤如下:
1. 初始化窗口的左右边界(例如 `left` 和 `right` 指针),并根据问题需求定义窗口内需要维护的变量(如窗口内的和、计数等)。
2. 移动右边界 `right`,将新元素加入窗口,并更新窗口内的变量。
3. 检查当前窗口是否满足条件。如果满足,记录答案(如更新最大/最小值),然后移动左边界 `left` 以缩小窗口,继续寻找其他符合条件的窗口。
4. 重复步骤 2 和 3,直到右边界遍历完数组。
应用场景
滑动窗口常用于以下几类问题:
- 子数组和问题(如最大、最小和)
- 子字符串问题(如最长无重复子串)
- 符合条件的区间统计
这种方法在处理需要频繁访问连续子序列的问题时具有高效性。
2. 长度最小的子数组
一、题目介绍
二、思路解析
方法一:暴力枚举
枚举 将数组中的每个元素 都当做起点,向后遍历数组寻找最短区间,最后找到将所有元素当做期待所得结果的最小值。
优化方法:滑动窗口 :
由于此问题分析的对象是「⼀段连续的区间」,因此可以考虑「滑动窗口」的思想来解决这道题。
(1)left = 0, right = 0
(2)进窗口
- 当窗口中的值小于target是,进窗口
(3)判断什么时候出窗口并更新结果
- 当窗口中的值大于等于target时,窗口中的值减去left位置的值(更新结果),出窗口(left++)
三、代码实现
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;}
};
. - 力扣(LeetCode)
3. 无重复字符的最长子串
一、题目介绍
二、思路解析
方法一:暴力枚举 + 哈希表
方法二:滑动窗口 + 哈希表:
(1)left = 0, right = 0
- 定义一个数组模拟哈希表来记录每个字符出现的次数。
(2)进窗口
- 每当字符串出现一次时,数组中对应位置大小+1,来记录字符出现的次数
(3)判断什么时候出窗口并更新结果
- hash[s[right]]大于1,那就说明此时字符s[right]与 [left到right) 之间的字符有重复。这时开始移动left(出窗口),并且hash[s[left]]-1,这样当left移动到有重复的字符并-1时,循环结束。开始更新结果。
- hash[s[right]]没有超过1,则说明没有窗口间重符字符,更新结果,继续移动窗口right++
三、代码实现
class Solution {
public:int lengthOfLongestSubstring(string s) {int hash[128] = { 0 };int left = 0, right = 0, n = s.size();int ret = 0;while (right < n){hash[s[right]]++;while (hash[s[right]] > 1)hash[s[left++]]--;ret = max(ret, right - left + 1);right++;}return ret;}
};
. - 力扣(LeetCode)
4. 最大连续1的个数|||
一、题目介绍
二、思路解析
滑动窗口:
(1)left = 0, right = 0
- 初始化left,right,zero。
(2)进窗口
- 遍历数组,当该元素是0时,zero++(进窗口)。
(3)判断什么时候出窗口并更新结果
- 如果此时zero大于k,left开始移动(出窗口),直到窗口中的0小于k,循环结束后更新结果。
- zero不大于k,则继续进窗口,不断更新最长连续空间的最大值。
三、代码实现
class Solution {
public:int longestOnes(vector<int>& nums, int k) {int left = 0, right = 0, zero = 0;int ret = 0, n = nums.size();while (right < n){if (nums[right] == 0) zero++; //进窗口while (zero > k)if(nums[left++] == 0) zero--;//出窗口ret = max(ret, right - left + 1); //更新结果right++;}return ret;}
};
. - 力扣(LeetCode)
5. 将x减小到零的最小操作
一、题目介绍
二、思路解析
滑动窗口 + 哈希表:
(1)left = 0, right = 0
- 定义一个tmp用来存储当前窗口中的数之和,len来存储最长窗口
(2)进窗口
- 当right < n 时,tmp不断的加上当前right位置的值(进窗口)
(3)判断什么时候出窗口并更新结果
- 当窗口中的值tmp大于target时,tmp减去left位置的值,left++(出窗口)。如果窗口重的值等于target则更新结果。
三、代码实现
class Solution {
public:int minOperations(vector<int>& nums, int x) {int left = 0, right = 0;int sum = 0, n = nums.size();for (int& e : nums) sum += e;int target = sum - x;if (target < 0) return -1;else if (target == 0) return n;int tmp = 0, len = -1;while (right < n){ tmp += nums[right];// 入窗口while (tmp > target) tmp -= nums[left++]; //出窗口if (tmp == target) len = max(len, right - left + 1); //更新结果right++;}if (len == -1) return -1;else return n - len;}
};
. - 力扣(LeetCode)
6. 水果成篮
一、题目介绍
注意:0、1、2、3分别表示是一种水果,而不是拥有水果的类型。例如0表示苹果,1表示香蕉,2表示西瓜,3表示鸭梨
二、思路解析
暴力枚举 + 哈希表
枚举 将数组中的每一个元素都当做起点开始向后遍历,直到哈希表中的值大于2,比较所有枚举结果中的最大数目
滑动窗口 + 哈希表:
(1)left = 0, right = 0
- 定义一个哈希表用来记录窗口中采摘水果的中类
(2)进窗口
- 每采摘一棵树,该树代表的水果对应哈希表中的位置+1(进窗口)。
(3)判断什么时候出窗口并更新结果
- 当hash的大小大于2时,说明此时窗口中的水果的种类超过了两种,这时开始移动left(出窗口),hash[left]--,当hash[left] 等于0时,说明窗口内没有这种水果了,删除hash表中的该种水果,更新结果。
- 当hash的大小不大于2,说明此时窗口内水果的种类没有超过2,则更新结果,继续进窗口
三、代码实现
class Solution {
public:int totalFruit(vector<int>& fruits) {unordered_map<int, int> hash; //统计窗口内出现了几种水果int left = 0, right = 0;int n = fruits.size(), ret = 0;while (right < n){hash[fruits[right]]++; // 进窗口while (hash.size() > 2) //判断{//出窗口hash[fruits[left]]--;if (hash[fruits[left]] == 0)hash.erase(fruits[left]);left++;}ret = max(ret, right - left + 1);//更新结果right++;}return ret;}
};
. - 力扣(LeetCode)
7. 找到字符串中所有字母异位词
一、题目介绍
二、思路解析
暴力枚举 + 哈希表
枚举 字符串中每三个字符 和p进行比较。用hash表记录每三个字符的种类和数量,和p进行比较。
滑动窗口 + 哈希表:
(1)left = 0, right = 0
定义两个hash表,一个用来保存 s 中的子串每个字符出现的个数,另⼀个来保存 p 中每⼀个字符出现的个数。再定义一个count用来表示窗口中符合p的异位词的个数
(2)进窗口
- for循环,每循环一次进一次窗口
(3)判断什么时候出窗口并更新结果
- 刚进窗口的字符是p中的异位词,并且此时窗口中该字符的数量小于p中该字符的数量(例如如果刚进窗口中字符是a,p中也有一个a,并且窗口前两个字符都不是a。符合条件),count++,表示窗口中是p中异位词的数量多了一个。如果此时窗口中该字符的数量大于p中该字符的数量,则不符合条件(例如窗口中【a, b, a】,p中【a, b , c】)。
- 刚进窗口中的字符不是p中的异位词,hash2[in- 'a'] <= hash1[in - 'a']也不成立(例如进窗口的字符是d,那么hash2【d - 'a'】 = 1,而p中没有d,那么hash1【d - ‘a’】= 0,定义hash表示,所有元素初始化为0,出现一次+1)
- 当窗口大小大于p的大小时,移动left,此时出窗口的字符如果是p的异位词并且窗口中该字符的数量不多于p中该字符的数量(例如上面的【a, b, a】,当移走第一个a时,hash表中字符a所在位置的大小为2,就不执行count--),count--,窗口中是p的异位词的数量-1,最后hash表中left移动前所在位置-1(出窗口)。
- 当count == p的大小时,说明窗口中都是p的异位词
三、代码实现
class Solution {
public:vector<int> findAnagrams(string s, string p) {vector<int> ret;//统计字符串p中每个字符的个数int hash1[26] = { 0 };for (auto e : p) hash1[e- 'a']++;//统计滑动窗口中中每个字符的个数int hash2[26] = { 0 };int count = 0;for (int left = 0, right = 0; right < s.size(); right++){char in = s[right];hash2[in - 'a']++;//进窗口if (hash2[in- 'a'] <= hash1[in - 'a']) count++;//维护countif (right - left + 1 > p.size()){char out = s[left++];if (hash2[out - 'a'] <= hash1[out - 'a']) count--;//维护counthash2[out - 'a']--;//出窗口}//更新结果if (count == p.size()) ret.push_back(left);}return ret;}
};
. - 力扣(LeetCode)
8. 串联所有单词的子串
一、题目介绍
二、思路解析
非就是之前处理的对象是⼀个⼀个的字符,我们这里处理的对象是⼀个⼀个的单词。小伙伴们可以照着上题的思路自己想一想做一做,做题思路这里不再赘述。
三、代码实现
class Solution {
public:vector<int> findSubstring(string s, vector<string>& words) {vector<int> ret;//跑存words中所有字符串的个数unordered_map<string, int> hash1;for (auto& st: words) hash1[st]++;int n = words[0].size(), m = words.size();for (int i = 0; i < n; i++){unordered_map<string, int> hash2;for(int left = i, right = i, count = 0; right + n <= s.size(); right += n){string in = s.substr(right, n);hash2[in]++;// 入窗口if (hash1[in] && hash2[in] <= hash1[in]) count++;// 维护count//判断if (right - left + 1 > n * m) {string out = s.substr(left, n);if (hash1[out] && hash2[out] <= hash1[out]) count--;// 维护counthash2[out]--;//出窗口left += n;}if (count == m) ret.push_back(left);}}return ret;}
};
9. 最小覆盖子串
一、题目介绍
二、思路解析
暴力枚举 + 哈希表
枚举 从中的每个字符开始向后遍历数组,直到找到覆盖t的位置,比较枚举的所有结果选取最小值。
滑动窗口 + 哈希表:
(1)left = 0, right = 0
定义两个hash表⼀个将目标串的信息统计起来,另⼀个哈希表动态的维护窗口内字符串的信息。定义一个kind,统计目标传中字符的种类。定义count统计窗口中能够覆盖t的字符种类的数量。
(2)进窗口
for循环,进窗口
(3)判断什么时候出窗口和更新结果
- 只有当窗口中的某一字符数等于t中的该字符的个数时,count才会+1(例如t中有两个A,那么只有窗口中出现第二个A时,才会执行count++,第一个A出现时不会执行count++)。
- 当count == kind时,说明此时窗口可以覆盖字符串t,更新结果,记录下此时窗口的起始位置和窗口的长度。然后判断是否count--,移动left,hash[left]--(出窗口)。
三、代码实现
class Solution {
public:string minWindow(string s, string t) {string ret;int hash1[128] = { 0 };// 统计t中每个字符出现的次数int kinds = 0; // 统计t中字符的种类for (auto& e : t) if ( hash1[e]++ == 0 ) kinds++;int hash2[128] = { 0 }; // 统计s中每个字符出现的次数int n = s.size(), m = t.size();int minlen = INT_MAX, begin = -1;for (int left = 0, right = 0, count = 0; right < n; right++){char in = s[right];hash2[in]++; // 入窗口if (hash2[in] == hash1[in]) count++;//维护countwhile (count == kinds){if (right - left + 1 < minlen)//更新结果{minlen = right - left + 1;begin = left;}char out = s[left++];if (hash2[out] == hash1[out]) count--;//维护counthash2[out]--;//出窗口}}if (begin == -1) return "";else return s.substr(begin, minlen);}
};
. - 力扣(LeetCode)
相关文章:

算法中的移动窗帘——C++滑动窗口算法详解
1. 滑动窗口简介 滑动窗口是一种在算法中常用的技巧,主要用来处理具有连续性的子数组或子序列问题。通过滑动窗口,可以在一维数组或字符串上维护一个固定或可变长度的窗口,逐步移动窗口,避免重复计算,从而提升效率。常…...

AcWing 3585:三角形的边 ← sort() 函数
【题目来源】 给定三个已知长度的边,确定是否能够构成一个三角形,这是一个简单的几何问题。 我们都知道,这要求两边之和大于第三边。 实际上,并不需要检验所有三种可能,只需要计算最短的两个边长之和是否大于最大那个就…...

阿里云-银行核心系统转型之业务建模与技术建模
业务领域建模包括业务建模和技术建模,整体建模流程图如下: 业务建模包括业务流程建模和业务对象建模 业务流程建模:通过对业务流程现状分析,结合目标核心系统建设能力要求,参考行业建 模成果,形成结构化的…...

MySQL核心知识:春招面试数据库要点
在前文中,我们深入剖析了MyBatis这一优秀的持久层框架,了解了它如何实现SQL语句与Java对象的映射,以及其缓存机制等重要内容。而作为数据持久化的核心支撑,数据库的相关知识在Java开发中同样至关重要。MySQL作为最流行的开源关系型…...

Hive之加载csv格式数据到hive
场景: 今天接了一个需求,将测试环境的hive数据导入到正式环境中。但是不需要整个流程的迁移,只需要迁移ads表 解决方案: 拿到这个需求首先想到两个方案: 1、将数据通过insert into语句导出,然后运行脚本 …...

Java web与Java中的Servlet
一。前言 Java语言大多用于开发web系统的后端,也就是我们是的B/S架构。通过浏览器一个URL去访问系统的后端资源和逻辑。 当我在代码里看到这个类HttpServletRequest 时 让我想到了Servlet,Servlet看上去多么像是Java的一个普通类,但是它确实…...

kafka常用目录文件解析
文章目录 1、消息日志文件(.log)2、消费者偏移量文件(__consumer_offsets)3、偏移量索引文件(.index)4、时间索引文件( .timeindex)5、检查点引文件( .checkpoint&#x…...

RV1126+FFMPEG推流项目源码
源码在我的gitee上面,感兴趣的可以自行了解 nullhttps://gitee.com/x-lan/rv126-ffmpeg-streaming-projecthttps://gitee.com/x-lan/rv126-ffmpeg-streaming-project...

ANSYS SimAI
ANSYS SimAI 是 ANSYS 公司推出的一款基于人工智能(AI)的仿真解决方案,旨在通过机器学习技术加速仿真流程,降低计算资源需求,并为用户提供更高效的工程决策支持。其核心目标是简化复杂仿真过程,帮助工程师快…...

hedfs和hive数据迁移后校验脚本
先谈论校验方法,本人腾讯云大数据工程师。 1、hdfs的校验 这个通常就是distcp校验,hdfs通过distcp迁移到另一个集群,怎么校验你的对不对。 有人会说,默认会有校验CRC校验。我们关闭了,为什么关闭?全量迁…...

蓝桥杯单片机(八)定时器的基本原理与应用
模块训练: 当有长定时情况时,也就是定时长度超过65.5ms时,采用多次定时累加 一、定时器介绍 1.单片机的定时/计数器 2.定时器工作原理 3.定时器相关寄存器 二、定时器使用程序设计 1.程序设计思路 与写中断函数一样,先写一个初…...

刷题总结 回溯算法
为了方便复习并且在把算法忘掉的时候能尽量快速的捡起来 刷完回溯算法这里需要做个总结 回溯算法的适用范围 回溯算法是深度优先搜索(DFS)的一种特定应用,在DFS的基础上引入了约束检查和回退机制。 相比于普通的DFS,回溯法的优…...

C++ 静态变量static的使用方法
static概述: static关键字有三种使用方式,其中前两种只指在C语言中使用,第三种在C中使用。 静态局部变量(C) 静态全局变量/函数(C) 静态数据成员/成员函数(C) 静态局部变量 静态局部变量&…...

Langchain+文心一言调用
import osfrom langchain_community.llms import QianfanLLMEndpointos.environ["QIANFAN_AK"] "" os.environ["QIANFAN_SK"] ""llm_wenxin QianfanLLMEndpoint()res llm_wenxin.invoke("中国国庆日是哪一天?") print(…...

20250124 Flink中 窗口开始时间和結束時間
增量聚合的 ProcessWindowFunction # ProcessWindowFunction 可以与 ReduceFunction 或 AggregateFunction 搭配使用, 使其能够在数据到达窗口的时候进行增量聚合。当窗口关闭时,ProcessWindowFunction 将会得到聚合的结果。 这样它就可以增量聚合窗口的…...

Android Studio安装配置
一、注意事项 想做安卓app和开发板通信,踩了大坑,Android 开发不是下载了就能直接开发的,对于新手需要注意的如下: 1、Android Studio版本,根据自己的Android Studio版本对应决定了你所兼容的AGP(Android…...

设计模式Python版 单例模式
文章目录 前言一、单例模式二、单例模式实现方式三、单例模式示例四、单例模式在Django框架的应用 前言 GOF设计模式分三大类: 创建型模式:关注对象的创建过程,包括单例模式、简单工厂模式、工厂方法模式、抽象工厂模式、原型模式和建造者模…...

7-Zip高危漏洞CVE-2025-0411:解析与修复
7-Zip高危漏洞CVE-2025-0411:解析与修复 免责声明 本系列工具仅供安全专业人员进行已授权环境使用,此工具所提供的功能只为网络安全人员对自己所负责的网站、服务器等(包括但不限于)进行检测或维护参考,未经授权请勿利…...

python实现http文件服务器访问下载
//1.py import http.server import socketserver import os import threading import sys# 获取当前脚本所在的目录 DIRECTORY os.path.dirname(os.path.abspath(__file__))# 设置服务器的端口 PORT 8000# 自定义Handler,将根目录设置为脚本所在目录 class MyHTT…...

《一文讲透》第4期:KWDB 数据库运维(6)—— 容灾与备份
一、KWDB 容灾 WAL 概述 KWDB 采用预写式日志(Write-Ahead Logging,WAL),记录每个时序表的模式变更和数据变更,以实现时序数据库的数据灾难恢复、时序数据的一致性和原子性。 KWDB 默认会将保存在 WAL 日志缓存中的…...

ArcGIS10.2 许可License点击始终启动无响应的解决办法及正常启动的前提
1、问题描述 在ArcGIS License Administrator中,手动点击“启动”无响应;且在计算机管理-服务中,无ArcGIS License 或者License的启动、停止、禁止等均为灰色,无法操作。 2、解决方法 ①通过cmd对service.txt进行手动服务的启动…...

Level2逐笔成交逐笔委托毫秒记录:今日分享优质股票数据20250124
逐笔成交逐笔委托下载 链接: https://pan.baidu.com/s/1UWVY11Q1IOfME9itDN5aZA?pwdhgeg 提取码: hgeg Level2逐笔成交逐笔委托数据分享下载 通过Level2逐笔成交与逐笔委托的详细数据,这种以毫秒为单位的信息能揭示许多关键点,如庄家意图、误导性行为…...

概率密度函数(PDF)分布函数(CDF)——直方图累积直方图——直方图规定化的数学基础
对于连续型随机变量,分布函数(Cumulative Distribution Function, CDF)是概率密度函数(Probability Density Function, PDF)的变上限积分,概率密度函数是分布函数的导函数。 如果我们有一个连续型随机变量…...

YOLOv5训练自己的数据及rknn部署
YOLOv5训练自己的数据及rknn部署 一、下载源码二、准备自己的数据集2.1 标注图像2.2 数据集结构 三、配置YOLOv5训练3.1 修改配置文件3.2 模型选择 四、训练五、测试六、部署6.1 pt转onnx6.2 onnx转rknn 七、常见错误7.1 训练过程中的错误7.1.1 cuda: out of memory7.1.2 train…...

计算机图形学:实验四 带纹理的OBJ文件读取和显示
一、程序功能设计 在程序中读取带纹理的obj文件,载入相应的纹理图片文件,将带纹理的模型显示在程序窗口中。实现带纹理的OBJ文件读取与显示功能,具体设计如下: OBJ文件解析与数据存储 通过实现TriMesh类中的readObj函数&#x…...

SQL Server 使用SELECT INTO实现表备份
在数据库管理过程中,有时我们需要对表进行备份,以防数据丢失或修改错误。在 SQL Server 中,可以使用 SELECT INTO 语句将数据从一个表备份到另一个表。 备份表的 SQL 语法: SELECT * INTO 【备份表名】 FROM 【要备份的表】 SEL…...

【线性代数】基础版本的高斯消元法
[精确算法] 高斯消元法求线性方程组 线性方程组 考虑线性方程组, 已知 A ∈ R n , n , b ∈ R n A\in \mathbb{R}^{n,n},b\in \mathbb{R}^n A∈Rn,n,b∈Rn, 求未知 x ∈ R n x\in \mathbb{R}^n x∈Rn A 1 , 1 x 1 A 1 , 2 x 2 ⋯ A 1 , n x n b 1…...

Python标准库 threading 的 start 和 join 的使用
python 的多线程机制可以的适用场景不适合与计算密集型的,因为 GIL 的存在,多线程在处理计算密集型时,实际上也是串行的,因为每个时刻只有一个线程可以获得 GIL,但是对于 IO 处理来说,不管是网络IO还是文件…...

无公网IP 外网访问媒体服务器 Emby
Emby 是一款多媒体服务器软件,用户可以在 Emby 创建自己的个人多媒体娱乐中心,并且可以跨多个设备访问自己的媒体库。它允许用户管理传输自己的媒体内容,比如电影、电视节目、音乐和照片等。 本文将详细的介绍如何利用 Docker 在本地部署 Emb…...

【数据结构】_顺序表
目录 1. 概念与结构 1.1 静态顺序表 1.2 动态顺序表 2. 动态顺序表实现 2.1 SeqList.h 2.2 SeqList.c 2.3 Test_SeqList.c 3. 顺序表性能分析 线性表是n个具有相同特性的数据元素的有限序列。 常见的线性表有:顺序表、链表、栈、队列、字符串等;…...