滑动窗口的初步了解

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

本篇主要是讲的是滑动窗口的两种类型。


定义:滑动窗口

什么是滑动窗口呢?就像它的名字一样,是一个滑动的窗口。
在这里插入图片描述

一、定长滑动窗口

对于定长滑动窗口,总体来说是简单一些的,就是由于长度定了,才使得接下来解题更方便。

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++;              // 计数器加1
            max1 = max(ans, max1);  // 更新最大值
        }
        
        // 滑动窗口:从第k个字符开始,逐个移动窗口
        for(int i = k; i < n; i++) {
            if(m.count(p.front()))  // 如果窗口最左侧字符是元音
                ans--;              // 移除该字符后计数器减1
            p.pop_front();          // 移除窗口最左侧字符
            p.push_back(s[i]);      // 添加新字符到窗口右侧
            if(m.count(s[i]))       // 如果新字符是元音
                ans++;              // 计数器加1
            max1 = 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.最长和谐子序列

题目传送门:最长和谐子序列
![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/d1cdf06774914c4f887a0d429906503f.png
由于题目所说并不是连续子序列,所以可以先进行排序之后再进行操作
解决代码:

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;
    }
};

在这里插入图片描述


总结

当然,经过这些题目的练习,还是只能做一些浅显的题目,对于那些条件特别复杂的,还是很难理清,希望,经过接下来的练习可以更精进一些。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值