【算法】滑动窗口
滑动窗口
1.概念
滑动窗口是一类很常见的题型,最常见的就是子串问题,因为滑动窗口是一个连续的,所以很容易就是问满足条件的最大或者最小子串啊,这个条件就是不同的地方,但万变不离其宗,滑动窗口就是一个窗口的移动。
总之:子串+最值 --> 滑动窗口
滑动窗口有两大类
- 固定长度的滑动窗口:窗口的大小是固定好的,这是其实分为了窗口形成和人窗口滑动两个过程,窗口形成就是要先让窗口达到要求的长度; 窗口滑动的过程在右边界长的时候,左边界也要跟着长,维持窗口长度不变;
- 可变长度的滑动窗口:这也是遇到最多的,控制窗口移动的原因不是长度,而是是否达到题目中某一条件,右边窗口一直在尝试移动,试图寻找到一个最值,然后可能会超出,这时候右边不动了,左边去动,左边得去移动重新让整个窗口满足题目中的条件。然后右边就又能移动了。接着跃跃欲试的去达到一个最值;
其实滑动窗口也被很形象的称为“毛毛虫”模型,就像毛毛虫在爬,右边脚先去动,动不了了左边脚去动,左边动不了,右边再动,就这样慢慢向前移;
- 最长子串:右边界不断移动寻找最优解,直至破坏了条件,左边界开始移动,然后满足条件,寻找可行解
- 最小子串:右边界不断移动寻找可行解,然后满足了条件,左边界开始移动,寻找最优解,直至破坏了条件,右边界再去移动重新使满足条件
2.过程
- 1.从题目中先整理出条件,常见的比如说子序列的和大于某值,子串中包含某些值,子串中出现了重复值,先把这些条件找出来,这就是用来移动窗口的依据;
- 2.初始化left和right指针都为0,right指针从头走到尾,当不满足上面的条件时,right走,一直走到这个窗口满足上面我们总结出来的条件了,停下;
- 3.记录下我们要的答案,比如最典型的问子串的最小长度啊啥的,记录下这时候我们的right-left+1,这就是目前窗口的大小;
- 4.右边走不动了那左边就得开始走了,移动左指针,每次移动都要把左指针的值给去除,因为我们统计的结果只能是窗口里的结果,出去了自然就不要了,然后,每一次左指针的移动都要判断是否满足条件,比如要求子串和大于某一值,左边移动一步,减去这个值看还大于目标值吗,如果满足,更新我们最终要的最小子串,因为这时候子串长度肯定缩小了嘛,直到不满足条件为止;如果不满足,那左指针不用再动了,右指针可以开始动了,寻找下一个满足条件时停下来。
- 5.就这样重复,右边动完左边动,左边动完右边再动;
3.模板
int left = 0, right = 0;
while(right < nums.length){
result = result + nums[right];
//更新窗口内数据;
//将移入窗口的值添加进结果,这里就是不同题目不同要求;比如和,比如哈希表等;
while(判断是否达到条件){ //如果达到条件左窗口就要收缩了;
res = Math.min(res, right-left+1); //只要满足条件每次都要更新答案;
//更新窗口内数据;
result = result - nums[left]; //左窗口的值从结果去除;
left++; //左窗口移动;
}
right++; //只要不满足条件右窗口移动;
}
其实,我们的右边界就是在努力满足条件,找到一个可行解,而我们的左边界呢,想让这个可行解更好一点,比如长度更短一下,是在寻找一个更好的最优解,但是很可能玩脱了不满足条件。
在套模板的时候我们需要思考以下问题:
- 1.当移动right,即加入结果的时候,需要更新哪些数据;
- 2.到达什么条件时,窗口停止扩大,也就是右边界停下来,开始左边界移动,缩小窗口;
- 3.当移动left,即移出结果的时候,需要更新哪些数据;
- 4.要的最后答案应该在窗口扩大还是窗口缩小时更新;
4.样例
固定长度的滑动窗口:
变长的滑动窗口
5.体会
- 要能够根据题目要求找到条件,因为条件是指挥我们移动左窗口还是右窗口的长官,不满足条件,右窗口移动; 满足条件,左窗口移动;
- 其实主要的思路都是这样的,难点就会在于这里面会结合很多其他的知识点,比如哈希表,单调等,还有就是左边窗口的移动,要能够灵活应变
- if按照常规的没有办法解决了,if确定是滑动窗口的思路,那其实可以想一下队列,因为滑动窗口其实就是一种队列,当换个角度进行思考的时候,可能就能够解决了。
- 要始终清楚左右两个边界都是不会回退的,都是朝着最后走的,不可能会出现往回走的时候;
- 两个窗口不会同时移动,每次只有两个窗口中的一个移动。
- 换种思路:我们的窗口滑动其实就是一个双端队列。当不满足条件时,数组元素依次从队尾入队;当满足条件时,队首元素出队;所以有的时候去想象成一个队列,可能会更好的理解;