二月有春节假期,在假期期间LeetCode十分仁慈的都给的是简单难度的题目,同时很多题目都是用滑动窗口方法来求解的:
2021/02/02 每日一题 替换后的最长重复字符
2021/02/03 每日一题 滑动窗口中位数
2021/02/04 每日一题 子数组最大平均数 I
2021/02/09 每日一题 K 个不同整数的子数组
2021/02/10 每日一题 字符串的排列
2021/02/15 每日一题 最大连续1的个数 (2种方法)
2021/02/18 每日一题 K 连续位的最小翻转次数 (直接翻转+滑动窗口)
2021/02/19 每日一题 最大连续1的个数 III
2021/02/21 每日一题 绝对差不超过限制的最长连续子数组
2021/02/23 每日一题 爱生气的书店老板
2021/02/28 每日一题 单调数列
一个月总结一个方法吧,上个月是并查集,这个月是滑动窗口,总结下方便日后回顾和记忆
使用滑动窗口的场景
总结上面的题目可以发现滑动窗口主要用于字符串和数组数据,当需要获取这些数组内部子串的情况,就会用到滑动窗口,来获取不同子串的解决问题。
同时滑动窗口能够简化for循环的使用,优化for循环的嵌套,降低时间复杂度。
滑动窗口的实现
滑动窗口的建立:所有题目滑动窗口都是通过两个指针来实现的left
和right
,两个指针分别对应的是窗口的左右边界,移动窗口也是在循环中不断增加left
和right
的值。
滑动窗口的边界:滑动窗口的right
移动到原数组的左侧的时候,那么就是滑动窗口的边界,一般用while(right < length)
这种条件来判断窗口的滑动边界,当right
的数值和传入数组(字符串)长度相同的时候,就是已经到顶了,那么就要跳出循环。
滑动窗口的关键问题
通过上面两点已经知道:
- 求子串问题的时候用滑动窗口
- 滑动窗口用指针实现,并且右指针到达数组(字符串)末尾的时候停止循环
那么接下来就是几个滑动窗口最关键的问题,平时使用的时候总结出这几个问题,基本上做完了,以2021/02/03 每日一题 滑动窗口中位数这道题目为例
while(right<nums.length) {
if(right-left+1 < k) {
window = windowSort(window,nums[right])
} else if(right-left+1 === k) {
window = windowSort(window,nums[right])
res.push(find(window,k))
} else {
window =windowDelete(window,nums[left])
left++
window = windowSort(window,nums[right])
res.push(find(window,k))
}
right++
}
这里的windowSort()
方法是传入数据,并且排序数组
windowDelete()
方法是删除对应的数据
while(right<nums.length){}
对应的是滑动窗口的边界问题
当窗口扩展的时候(即当前窗口小于等于设定大小),窗口内的数据需要如何操作?
当right-left+1 < k
时候,即当前窗口还没有达到设定值k
的时候,是移入数据并且排序
当right-left+1 === k
的时候,即窗口第一次成立的时候,窗口大小正好等于K的时候,是移入数据,并且第一次获取中位数
计算窗口大小用right - left + 1
,因为数组是从0开始计数的,并且当right=0,left=0
的时候窗口大小为1
什么时候指针left
要右移(窗口缩小)?
当right-left+1 === k
的时候,进行移入数据和取中位数的操作,之后right++
,窗口继续扩展了,那么right-left+1 > k
窗口的大小就大于设定的值了,通过left++
来实现窗口右移
当窗口缩小的时候,窗口内的数据需要如何操作?
窗口left
右移(窗口缩小)的时候,其实是将之前left
对应的值移出当前窗口,维护当前窗口的大小始终等于设定值。
对应到上面的程序就是
window =windowDelete(window,nums[left])
left++
用windowDelete()
方法是删除对应left
的数据,之后left++
维护窗口,之后其实是重复right-left+1 === k
的操作
什么时候更新结果
这个需要根据题意,这道题是需要在每次窗口成立的时候获取中位数,同时还有可能每次窗口成立的时候获取窗口内所有数的最大值之类的,通常有使用Math.max()
和Math.min()
来对比上个窗口与当前窗口的值取最大(最小)实现操作
总的来说
一般是在窗口扩展的时候进行数据操作,对应right++
并且right - left + 1 < k
的过程
然后当窗口达到设定大小的时候记录一次数据oldvalue
,对应right - left + 1 === k
的时候
之后因为right++
窗口继续扩展
会导致窗口的大小大于设定对应right - left + 1 > k
,就会进行窗口的缩小,对应的就是left++
,实现left
的右移,并且要将右移的数从原来的结果减掉,新right
对应的数也要添加进结果,这样操作之后会产生一个新的值newvalue
根据需要处理oldvalue
和newvalue
即可
循环结束的标准是right >= length
,当右指针移动到数组(字符串)最右端就结束循环