【算法】滑动窗口

滑动窗口

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.样例

固定长度的滑动窗口:

567. 字符串的排列

剑指 Offer 59 - I. 滑动窗口的最大值

变长的滑动窗口

3. 无重复字符的最长子串

209. 长度最小的子数组

424. 替换后的最长重复字符

76.最小覆盖子串

159.至多包含两个不同字符的最长子串

340.至多包含 K 个不同字符的最长子串

30.串联所有单词的子串

5.体会

  • 要能够根据题目要求找到条件,因为条件是指挥我们移动左窗口还是右窗口的长官,不满足条件,右窗口移动; 满足条件,左窗口移动;
  • 其实主要的思路都是这样的,难点就会在于这里面会结合很多其他的知识点,比如哈希表,单调等,还有就是左边窗口的移动,要能够灵活应变
  • if按照常规的没有办法解决了,if确定是滑动窗口的思路,那其实可以想一下队列,因为滑动窗口其实就是一种队列,当换个角度进行思考的时候,可能就能够解决了。
  • 要始终清楚左右两个边界都是不会回退的,都是朝着最后走的,不可能会出现往回走的时候;
  • 两个窗口不会同时移动,每次只有两个窗口中的一个移动。
  • 换种思路:我们的窗口滑动其实就是一个双端队列。当不满足条件时,数组元素依次从队尾入队;当满足条件时,队首元素出队;所以有的时候去想象成一个队列,可能会更好的理解;
posted @ 2021-08-12 10:00  Curryxin  阅读(1735)  评论(1编辑  收藏  举报
Live2D