滑动窗口的初步了解
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
- 前言
- 定义:滑动窗口
- 一、定长滑动窗口
- 1.定长子串中元音的最大数目
- 2.子数组最大平均数1
- 3.大小为K且平均值大于等于阈值的子数组数目
- 二、不定长滑动窗口
- 1.无重复的最长字符串
- 2.最长和谐子序列
- 3.乘积小于k的子数组
- 总结
前言
本篇主要是讲的是滑动窗口的两种类型。
定义:滑动窗口
什么是滑动窗口呢?就像它的名字一样,是一个滑动的窗口。
一、定长滑动窗口
对于定长滑动窗口,总体来说是简单一些的,就是由于长度定了,才使得接下来解题更方便。
1.定长子串中元音的最大数目
题目传送门:定长子串中元音的最大数目
解决代码:
class Solution {
public:int maxVowels(string s, int k) {int n = s.size(); // 字符串长度int ans = 0; // 当前窗口内元音字母的数量unordered_map<char, int> m; // 用于快速判断字符是否为元音的哈希表m['a'] = 1;m['e'] = 1;m['i'] = 1;m['o'] = 1;m['u'] = 1;deque<char> p; // 存储当前窗口中的字符(实际上可以优化掉)int max1 = 0; // 记录最大元音数量// 初始化第一个窗口(前k个字符)for(int i = 0; i < k; i++) {p.push_back(s[i]); // 将字符加入窗口if(m.count(s[i])) // 如果是元音字母ans++; // 计数器加1max1 = max(ans, max1); // 更新最大值}// 滑动窗口:从第k个字符开始,逐个移动窗口for(int i = k; i < n; i++) {if(m.count(p.front())) // 如果窗口最左侧字符是元音ans--; // 移除该字符后计数器减1p.pop_front(); // 移除窗口最左侧字符p.push_back(s[i]); // 添加新字符到窗口右侧if(m.count(s[i])) // 如果新字符是元音ans++; // 计数器加1max1 = max(ans, max1); // 更新最大值}return max1; // 返回最大元音数量}
};
对于这一题关键就是维持窗口的大小,同时还要注意窗口中的值的变化,对于结果的影响。
2.子数组最大平均数1
题目传送门:子数组最大平均数1
解决代码:
class Solution {
public:double findMaxAverage(vector<int>& nums, int k) {int n = nums.size(); // 数组长度deque<double> p; // 存储当前窗口中的元素(可优化)double sum = 0; // 当前窗口的元素和double ans = -1e5; // 最大平均值(初始化为一个很小的值)// 预处理:先将前k-1个元素加入窗口for(int i = 0; i < k-1; i++) {p.push_back(nums[i]); // 将元素加入队列sum += nums[i]; // 累加元素和}// 滑动窗口:从第k-1个元素开始,逐个移动窗口for(int i = k-1; i < n; i++) {p.push_back(nums[i]); // 添加新元素到窗口右侧sum += nums[i]; // 更新窗口和// 计算当前窗口的平均值并更新最大值ans = max(ans, sum / k);// 移除窗口最左侧元素,为下一次滑动做准备sum -= p.front(); // 减去左侧元素的值p.pop_front(); // 移除左侧元素}return ans; // 返回最大平均值}
};
其实这一题与上面的题差不多。
3.大小为K且平均值大于等于阈值的子数组数目
题目传送门:大小为K且平均值大于等于阈值的子数组数目
解决代码:
class Solution {
public:int numOfSubarrays(vector<int>& arr, int k, int threshold) {int n = arr.size(); // 数组长度double sum = 0; // 当前窗口的元素和int ans = 0; // 符合条件的子数组数量deque<double> p; // 存储窗口元素(可优化掉)// 预处理:计算前k-1个元素的和for(int i = 0; i < k-1; i++) {sum += arr[i];p.push_back(arr[i]); // 将元素加入队列(后续会被移除)}// 滑动窗口处理:从第k-1个元素开始遍历到数组末尾for(int i = k-1; i < n; i++) {sum += arr[i]; // 窗口右侧新增元素p.push_back(arr[i]); // 将元素加入队列(可优化)// 判断当前窗口平均值是否达标if(sum / k >= threshold) {ans++; // 达标则计数器加1}// 窗口左边界右移一位sum -= p.front(); // 减去窗口左侧元素p.pop_front(); // 移除队列头部元素(可优化)}return ans; // 返回符合条件的子数组数量}
};
二、不定长滑动窗口
对这一类题目,写这一些题目时,我更倾向于嵌套大佬的模板
对于这一类,通常是把问题分解成两个状态,以此来进行窗口的变化,
1.无重复的最长字符串
题目传送门:无重复的最长字符串
解决代码:
class Solution {
public:int lengthOfLongestSubstring(string s) {int n=s.size();int ans=0;unordered_map<char,int>m;//来记录是否出现重复字符for(int left=0,right=0;right<n;right++){m[s[right]]++;//每次都存一下字符出现的次数while(m[s[right]]>1)//如果最右边的字符出现多次进行循环{m[s[left]]--;//最左边的下标向右移动left++;}ans=max(ans,right-left+1);//每次都判断一下ans的最大值}return ans;}
};
2.最长和谐子序列
题目传送门:最长和谐子序列
由于题目所说并不是连续子序列,所以可以先进行排序之后再进行操作
解决代码:
class Solution {
public:int findLHS(vector<int>& nums) {int n=nums.size();int ans=0;sort(nums.begin(),nums.end());//先进行排序,之后寻找满足条件的子数组就简单的一些for(int left=0,right=0;right<n;right++){while((nums[right]-nums[left])>1)//其最大值与最下值的差值如果大于1就将左下标进行向右移动{left++;}if(nums[right]-nums[left]==1)//如果满足条件就进行答案的更新。ans=max(right-left+1,ans);}return ans;}
};
3.乘积小于k的子数组
题目传送门:乘积小于k的子数组
对于这一题同样套用上面的模板,其中的条件也要进行改变一下,其中要主义left要小于等于right
解决代码:
class Solution {
public:int numSubarrayProductLessThanK(vector<int>& nums, int k) {int n=nums.size();int ans=0;int sum=1;for(int left=0,right=0;right<n;right++){sum*=nums[right];while(sum>=k&&left<=right)//满足这些条件时,将左下标向右移动,同时也要将数据移除。{sum=sum/nums[left];left++;} ans+=right-left+1;//至于为什么该操作可以计算出结果,请看下面的解释}return ans;}
};
总结
当然,经过这些题目的练习,还是只能做一些浅显的题目,对于那些条件特别复杂的,还是很难理清,希望,经过接下来的练习可以更精进一些。