基础算法:滑动窗口

目录

一、长度最小的子数组

二、无重复字符的最长子串

三、最大的连续1的个数III

四、将x减到0的最小操作数

五、水果成篮

六、找到字符串中所有字母异位词

七、串联所有单词的子串

八、最小覆盖子串


滑动窗口算法,本质是根据单调性,利用同向双指针创建窗口,然后遍历数组。

一、长度最小的子数组

209. 长度最小的子数组 - 力扣(LeetCode)

创建int变量ans,left和right首先设置在0位置,创建sum,sum+=[right],判断是否大于等于我的target,不是right++,继续判断,等到sum>=target时,right后的数据不再需要遍历,因为数组所有元素为正,遍历后边的数组只会让数据变大,由此暂停,找到第一个,记录len,len修改:只需将ans等于ans和right-left+1的min即可)sum减去【left】left可以往后遍历,判断,大于继续上述操作,小于right++,sum+【right】。即每次出窗口进窗口更新sum判断是否更新len判断是否更新ans。时间复杂度为n。

特殊情况下所有数值加起来都不够超过target,此时ans并未操作一次,判断是否等于初始值,等于返回0,不等于返回本身。

cpp 复制代码
class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        int ans=INT_MAX,left=0,right=0,sum=0;
        int n=nums.size();
        while(right<n)
        {
            sum+=nums[right];       //进窗口
            while(sum>=target)      //持续 判断
            {
                ans=min(ans,right-left+1);      //直接用min即可    
                sum-=nums[left++];              //出窗口
            }
            ++right;        //结束判断,更新右窗口
        } 
        return ans==INT_MAX?0:ans;      //特殊情况处理
    }
};

二、无重复字符的最长子串

3. 无重复字符的最长子串 - 力扣(LeetCode)

对于重复字符我们可以创建一个哈希表来判断

创建left,right,均从0开始,right往右遍历,【right】存进哈希表(计数桶) ,判断有没有重复元素,没有继续遍历将数据进窗口,遇到重复元素暂停,更新len,将left更新到窗口内的重复元素位置的下一个位置因为这里涉及到遍历,我们还是得按一个一个过去,(比如上图a,已经是让第一个窗口变最大的右边界,left从d到a遍历撑死都不会比这个长度还大)然后right++,判断,重复操作。

其实这题还能转化成最终返回子串,只需要在过程中多更新起始位置即可,最后return s.substr( begin,length)

cpp 复制代码
class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        if(s=="")return 0;      //特殊情况直接返回提高效率
        int ans=0,left=0,right=0,hash[128]={0};//数组模拟哈希映射
        int n=s.size();
        while(right<n)
        {
            ++hash[s[right]];           //进窗口
            while(hash[s[right]]>1)
            {
                --hash[s[left++]];      //出窗口,到区间内重复元素下一位置
            }
            //写在出窗口外边,保证每一次的right操作后都能检查更新
            ans=max(ans,right-left+1);  //每次操作后都更新长度,直接max判定
            ++right;                    
        }
        return ans;
    }
};

三、最大的连续1的个数III

1004. 最大连续1的个数 III - 力扣(LeetCode)

法一:暴力+0计数器,暴力遍历同时统计0的个数,更新长度,0大于k时才截至,说明这个位置往前的最大连续1子串就这么长了.

法二:滑动窗口,基于暴力枚举的思路,就会发现这是一道很明显的滑动窗口题,特别之处在于对0的统计,创建left right,从0开始,往后遍历,if(!【right】)count+=1,更新长度,等到count>k,让while循环(count>k)让左开始回收窗口,if(!【left】)count-=1;由此大功告成,复杂度n.

cpp 复制代码
class Solution {
public:
    int longestOnes(vector<int>& nums, int k) {
        int n=nums.size();
        int left=0,right=0,count=0,ans=0;
        while(right<n)
        {
            if(!nums[right])++count;    //统计0
            while(count>k)
            {
                if(!nums[left++])--count;
            }
            ans=max(ans,right-left+1);      //更新长度,max自动判定
            ++right;
        }
        return ans;
    }
};

四、将x减到0的最小操作数

1658. 将 x 减到 0 的最小操作数 - 力扣(LeetCode)

保证一段连续的子数组的和为数组元素和减去x即可

这不就是滑动窗口,不多赘述

cpp 复制代码
class Solution {
public:
    int minOperations(vector<int>& nums, int x) {
        int n=nums.size();
        int left=0,right=0,sum=0,target,ans=-1;
        for(int& e:nums){sum+=e;}
        //特殊情况处理
        if(sum<x){return -1;}
        target=sum-x;
        sum=0;
        while(right<n)
        {
            sum+=nums[right];
            while(sum>target)
            {
                sum-=nums[left++];
            }
            if(sum==target){ans=max(ans,right-left+1);}
            ++right;
        }
        return ans<0?-1:n-ans;
    }
};

五、水果成篮

904. 水果成篮 - 力扣(LeetCode)

滑动窗口,突破点就在于如何表示元素种类大于2,

思路如下:

学完map后我们知道我们可以将key代表元素种类,进窗口利用原数组【right】,并利用map的特性将value++,从而定义了一个map的存储单位,利用size函数当大于2时,while循环,先让hash.fruits.left出窗口,如果hash.fruits.left==0,也就是value为0,erase hash【nums【left++】】即可。

cpp 复制代码
class Solution {
public:
    int totalFruit(vector<int>& fruits) {
        int left=0,right=0,ans=0;
        int n=fruits.size();
        map<int,int> hash;      //便于实现种类的统计
        while(right<n)
        {
            hash[fruits[right]]++;      //进窗口的同时相当于直接创建了一个hash单位
            while(hash.size()>2)
            {
                --hash[fruits[left]];       //不着急++left
                if(hash[fruits[left]]==0)   //判断value是否为0,为0删除这个hash单位,退出while
                {
                    hash.erase(fruits[left]);
                }
                ++left;
            }
            ans=max(ans,right-left+1);
            ++right;
        }
        return ans;
    }
};

六、找到字符串中所有字母异位词

438. 找到字符串中所有字母异位词 - 力扣(LeetCode)

异位词我们可以借助哈希表解决,

分析题干,即求和p一样长的区间,且区间内元素种类相同,且每个元素出现次数与之一致。创建left right=0,因为都是小写字母,创建 两个 hash【26】便于比对,利用ascii码来判断位置,我们可以创建一个变量count,count最终等于p的size,进窗口的元素如果是hash2里的,且这个数目小于hash2【该数-'a'】就说明有效,count++,right-left+1>m时要进行出窗口,出窗口判断出的元素hash1是否

count:匹配上的元素个数。

cpp 复制代码
class Solution {
public:
    vector<int> findAnagrams(string s, string p) {
        int hash1[26]={0},hash2[26]={0};
        //统计元素种类及其个数,方便后续判断
        for(char&e:p)
        {
            hash2[e-'a']++;
        }
        int left=0,right=0,count=0,m=p.size();
        vector<int> ans;
        while(right<s.size())
        {
            char in=s[right];
            //进窗口,维护count
            if(++hash1[in-'a']<=hash2[in-'a'])++count;
            while(right-left+1>m)
            {
                char out=s[left++];
                //出窗口,维护count
                if(hash1[out-'a']--<=hash2[out-'a'])--count;
            }
            if(count==m){ans.push_back(left);}
            ++right;
        }
        return ans;
    }
};

七、串联所有单词的子串

30. 串联所有单词的子串 - 力扣(LeetCode)

因为word里所有的string的大小都是一样的,然后要求时这个string内部顺序不变,不同string顺序可以改变,想象一下如果可以把每个string当作一个字母,这不就是上一道异位词的题目吗,将字串存进来的操作可以通过substr(pos,len)来实现。

cpp 复制代码
class Solution {
public:
    vector<int> findSubstring(string s, vector<string>& words) {
        //先统计数据
        unordered_map<string,int> hash1;
        for(auto&e:words){++hash1[e];}
        int left,right,len=words[0].size(),n=s.size(),count;
        unordered_map<string,int> hash2;
        vector<int> ans;
        //遍历len次,开始位置为0,1,2...len-1
        for(int i=0;i<len;++i)
        {
            left=right=i;
            //清零!
            count=0;
            hash2.clear();
            while(right+len<=n)
            {
                string in=s.substr(right,len);
                //如果hash1里没有in,还会有一步插入key,初始value的操作
                if(hash1.count(in)&&++hash2[in]<=hash1[in])++count;
                //>=时说明hash2里多存进来了一个单词
                while(right-left>=words.size()*words[0].size())
                {
                    //substr提取子串
                    string out=s.substr(left,len);
                    if(hash1.count(out)&&hash2[out]--<=hash1[out])--count;
                    left+=len;
                }
                if(count==words.size()){ans.push_back(left);}
                right+=len;
            }
        }
        return ans;
    }
};

八、最小覆盖子串

76. 最小覆盖子串 - 力扣(LeetCode)

一样的思路,存一个begin为left即可

cpp 复制代码
class Solution {
public:
    string minWindow(string s, string t) {
        unordered_map<char,int> hash1;
        for(auto&e:t){++hash1[e];}
        unordered_map<char,int> hash2;
        int left=0,right=0,count=0;
        //不要更新子串,更新子串起始位置和长度更方便
        int minlen=INT_MAX,begin=-1;
        while(right<s.size())
        {
            //进窗口,维护count
            if(hash1.count(s[right])&&++hash2[s[right]]<=hash1[s[right]])
            {++count;}
            //符合条件,析出子串
            while(count==t.size())
            {
                //如果字符不是t里的直接++left
                while(!hash1.count(s[left])){left++;}
                if(right-left+1<minlen)
                {
                    minlen=right-left+1;
                    begin=left;
                }
                //判断这个位置的left是不是有效的
                //有效->终止,寻找下一个相等的机会
                //无效->再进入循环->最终析出子串
                if(hash2[s[left]]--<=hash1[s[left]])--count;
                ++left;
            }
            ++right;
    }
    return begin==-1?"":s.substr(begin,minlen);
    }
};

此篇完。

相关推荐
Voyager_43 小时前
图像处理踩坑:浮点数误差导致的缩放尺寸异常与解决办法
数据结构·图像处理·人工智能·python·算法
文艺倾年3 小时前
【八股消消乐】手撕分布式协议和算法(基础篇)
分布式·算法
今天只学一颗糖4 小时前
Linux学习笔记--查询_唤醒方式读取输入数据
笔记·学习
GIS学姐嘉欣4 小时前
【智慧城市】2025年中国地质大学(武汉)暑期实训优秀作品(5):智慧矿产
学习·gis·智慧城市·webgis
万岳科技系统开发4 小时前
从源码优化外卖配送系统:算法调度、智能推荐与数据分析应用
算法·数据挖掘·数据分析
折翼的恶魔4 小时前
前端学习之样式设计
前端·css·学习
信奥卷王7 小时前
[GESP202503 五级] 原根判断
java·数据结构·算法
兮山与7 小时前
算法4.0
算法
nju_spy7 小时前
力扣每日一题(二)任务安排问题 + 区间变换问题 + 排列组合数学推式子
算法·leetcode·二分查找·贪心·排列组合·容斥原理·最大堆