滑动窗口详解

ops/2024/9/23 15:21:40/

目录

一、滑动窗口的特定步骤:

二、题目解析

1、⻓度最⼩的⼦数组---点击跳转题目

3、最⼤连续 1 的个数 III----点击跳转题目

4、将 x 减到 0 的最⼩操作数----点击跳转题目

5、⽔果成篮----点击跳转题目


滑动窗口是双指针算法中细分的一种,它由暴力枚举算法优化而来,解决的是同向双指针问题;当一个题目在暴力解法中发现枚举时其实可以不用把“指针”往回走,就可以使用滑动窗口的思想来解决。也就是滑动窗口通过题目的某种性质,忽略了很多无效枚举,维护两个指针间的区间保持某种性质,从而把时间复杂度从O(n^2)优化到O(n)

只要题目的研究对象是一段连续区间或者能转化为一段连续区间,就可以考虑用滑动窗口来做,思路不清晰时可以先想题目的暴力枚举,从中优化而来

一、滑动窗口的特定步骤:

  • 定义指针left、right
  • 确定如何进窗口
  • 判断(指针内区的某种性质进入循环)用while循环做判断
  • 通过判断不断出窗口,使得指针内的区间满足性质
  • 确定在哪一步更新结果

滑动窗口的代码形式是双重循环,外层是right指针遍历,内层是为了指针内区间满足某种性质而不断出窗口;虽然是双重循环,但是时间复杂度只有O(n)

二、题目解析

1、⻓度最⼩的⼦数组---点击跳转题目

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

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

▪ 如果窗⼝内元素之和不满⾜条件: right++ ,让下⼀个元素进⼊窗⼝。

代码:

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

时间复杂度:虽然代码是两层循环,但是我们的 left 指针和 right 指针都是不回退的,两者 最多都往后移动 n 次。因此时间复杂度是 O(n)

2、⽆重复字符的最⻓⼦串----点击跳转题目

暴力思路:枚举「从每⼀个位置」开始往后,⽆重复字符的⼦串可以到达什么位置。找出其中⻓度最⼤的即可。在往后寻找⽆重复⼦串能到达的位置时,可以利⽤「哈希表」统计出字符出现的频次,来判断什么时候⼦串出现了重复元素。

优化:研究的对象依旧是⼀段连续的区间,因此继续使⽤「滑动窗⼝」思想来优化。
让滑动窗⼝满⾜:窗⼝内所有元素都是不重复的。
做法:右端元素 ch 进⼊窗⼝的时候,哈希表统计这个字符的频次:
▪ 如果这个字符出现的频次超过 1 ,说明窗⼝内有重复元素,那么就从左侧开始划出窗⼝,
直到 ch 这个元素的频次变为 1 ,然后再更新结果。
▪ 如果没有超过 1 ,说明当前窗⼝没有重复元素,可以直接更新结果

代码:

class Solution {
public:int lengthOfLongestSubstring(string s) {int hash[128];//下标表示字符,数组存储字符个数,可以快速查找每个字符个数int l = 0, r = 0;int len = 0;while(r < s.size()){hash[s[r]]++;//进窗口while(hash[s[r]] > 1)//判断hash[s[l++]]--;//出窗口len = max(len,r - l + 1);//更新结果r++;}return len;}
};

3、最⼤连续 1 的个数 III----点击跳转题目

不要去想怎么翻转,不要把问题想的很复杂,这道题的结果⽆⾮就是⼀段连续的 1 中间塞了 k 个 0 嘛。 因此,我们可以把问题转化成:求数组中⼀段最⻓的连续区间,要求这段区间内 0 的个数不超 过 k 个。 比特就业课 既然是连续区间,可以考虑使⽤「滑动窗⼝」来解决问题。

滑动窗口的性质:区间内至多有k个0,维护这个性质即可

代码:

class Solution {
public://转化:找到最长的子数组,0的个数不超过k个int longestOnes(vector<int>& nums, int k) {int l = 0, r = 0, len = 0;int zero = 0;while(r < nums.size()){if(nums[r] == 0) zero++;//进窗口while(zero > k)//判断性质是否满足{if(nums[l++] == 0) zero--;//出窗口}len = max(len,r - l + 1);//性质满足时更新结果r++;}return len;}
};

4、将 x 减到 0 的最⼩操作数----点击跳转题目

题⽬要求的是数组「左端+右端」两段连续的和为 x 的最短数组,正难则反,我们可以转化成求数组内⼀段连续的、和为 sum(nums) - x 的最⻓数组。此时,就是熟悉的「滑动窗⼝」问题了

target = sum - x

滑动窗口的性质:区间内和为target

题目所求的最小操作数也就是 nums的个数减去最长子数组

代码:

class Solution {
public://转化:找到最长子数组,和为 sum - xint minOperations(vector<int>& nums, int x) {int sum=0, target, len = -1;// sum = accumulate(nums.begin(), nums.end(), 0);for(auto e : nums) sum += e;target = sum - x;if(target < 0) return -1; //sum < x的情况不可能把x减为0int l = 0, r = 0;long long s = 0;//区间内的和while(r < nums.size()){s += nums[r];//进窗口while(s > target)//需要的是s等于target{s -= nums[l++];//出窗口}if(s == target) //上面循环跳出来时s可能等于或小于targetlen = max(len,r - l + 1);//更新结果r++;}if(len == -1) return len;else return nums.size() - len;}
};

5、⽔果成篮----点击跳转题目

研究的对象是⼀段连续的区间,可以使⽤「滑动窗⼝」思想来解决问题。 让滑动窗⼝满⾜:窗⼝内⽔果的种类只有两种。

做法:右端⽔果进⼊窗⼝的时候,⽤哈希表统计这个⽔果的频次。这个⽔果进来后,判断哈希表的⼤⼩:

▪ 如果⼤⼩超过 2:说明窗⼝内⽔果种类超过了两种。那么就从左侧开始依次将⽔果划出窗 ⼝,直到哈希表的⼤⼩⼩于等于 2,然后更新结果;

▪ 如果没有超过 2,说明当前窗⼝内⽔果的种类不超过两种,直接更新结果 len。

代码:

class Solution {
public://找出一个最长子数组,数组中最多两种水果int totalFruit(vector<int>& fruits) {const int N = 1e5 + 10;//下标表示水果种类,数组元素表示区间中这种水果放了几次int hash[N] = {0};int l = 0, r = 0;int kinds = 0,len = 0;//kinds表示区间内水果种类数while(r < fruits.size()){if( hash[fruits[r]]++ == 0) kinds++;while(kinds > 2){//出窗口hash[fruits[l]]--;if(hash[fruits[l]] == 0) kinds--;l++;}len = max(len,r - l + 1);r++;}return len;}
};


http://www.ppmy.cn/ops/33616.html

相关文章

rust使用Atomic创建全局变量和使用

Mutex用起来简单&#xff0c;但是无法并发读&#xff0c;RwLock可以并发读&#xff0c;但是使用场景较为受限且性能不够&#xff0c;那么有没有一种全能性选手呢&#xff1f; 欢迎我们的Atomic闪亮登场。 从 Rust1.34 版本后&#xff0c;就正式支持原子类型。原子指的是一系列…

RabbitMQ知识点总结(一)

为什么要使用RabbitMQ? 异步&#xff0c;解耦&#xff0c;削峰。 异步 提高效率&#xff1b;一个挂了&#xff0c;另外的服务不受影响。 解耦 增加或减少服务比较方便。 削峰 每天0点到16点&#xff0c;A系统风平浪静&#xff0c;每秒并发数量就100个。结果每次到了16点到…

C语言 void 指针就是空指针吗?它有什么作⽤?

一、问题 这是⼀个在⾯试时很容易出现的问题&#xff0c;但是也是很多⼈混淆的问题&#xff0c;这个问题如何回答&#xff1f; 二、解答 void 指针⼀般称为通⽤指针&#xff0c;要与空指针严格区分。void 指针⽤于指向⼀个不属于任 何类型的对象&#xff0c;所以 void 指针称为…

临床+康复的一体化治疗服务,把握黄金康复时间

随着我院脑血管病人&#xff0c;重症病人及骨科病人康复需求的日渐增多&#xff0c;为了使每位住院患者在治疗原发病的同时&#xff0c;第一时间接受到康复治疗&#xff0c;提高病人的生活质量&#xff0c;降低致残率&#xff0c;我院康复治疗科在院领导的大力支持下&#xff0…

【JDBC】Apache DbUtils工具类使用

1 简介 Commons DbUtils是Apache 组织提供的一个对]DBC进行简单封装的开源工具类库&#xff0c;简化JDBC应用程序的开发&#xff0c;同时也不会影响程序的性能&#xff0c;是一个小巧简单实用的工具。对于数据表的读操作&#xff0c;可以把结果转换成List、Array、Set等java集…

青少年软件编程(C/C++)三级等级考试真题试卷(2024年3月)

2024.03 青少年软件编程&#xff08;C/C&#xff09;3级等级考试真题试卷 编程题第 1 题 我家的门牌号 我家住在一条短胡同里&#xff0c;这条胡同的门牌号从1开始顺序编号。 若所有的门牌号之和减去我家门牌号的两倍&#xff0c;恰好等于n&#xff0c;求我家的门牌号及总共有…

vue3 element-plus 让el-container占满屏幕

在刚开始用element-plus的布局时&#xff0c;发现无法占满屏幕&#xff1a; 在App.vue中添加如下css代码&#xff1a; <style>html, body, #app {margin: 0;padding: 0;height: 100%;} </style>同时布局代码所在的component如下所示&#xff1a; <template&g…

【介绍下IDM的实用功能】

&#x1f3a5;博主&#xff1a;程序员不想YY啊 &#x1f4ab;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f917;点赞&#x1f388;收藏⭐再看&#x1f4ab;养成习惯 ✨希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出…