滑动窗口专题
1004. 最大连续1的个数 III
解题思路
重点: 题意转换。把「最多可以把 K 个 0 变成 1,求仅包含 1 的最长子数组的长度」转换为 「找出一个最长的子数组,该子数组内最多允许有 K 个 0 」。
经过上面的题意转换,我们可知本题是求最大连续子区间,可以使用滑动窗口方法。滑动窗口的限制条件是:窗口内最多有 K 个 0。
代码思路:
1.使用 l 和 r 两个指针,分别指向滑动窗口的左右边界。
2.r 主动右移: r 指针每次移动一步。当nums[r]为 0,说明滑动窗口内增加了一个 0;
3.l 被动右移: 判断此时窗口内 0 的个数,如果超过了 k,则 l 指针被迫右移,直至窗口内的 0 的个数小于等于 k 为止。
4.滑动窗口长度最大值就是所求。
示例:
以 A= [1,1,1,0,0,0,1,1,1,1,0], K = 2 为例,下面的动图演示了滑动窗口的两个指针的移动情况。
见 1004. 最大连续1的个数 III - 负雪明烛 的题解
public int longestOnes(int[] nums, int k) { |
分享滑动窗口模板
《挑战程序设计竞赛》这本书中把滑动窗口叫做「虫取法」,我觉得非常生动形象。因为滑动窗口的两个指针移动的过程和虫子爬动的过程非常像:前脚不动,把后脚移动过来;后脚不动,把前脚向前移动。
我分享一个滑动窗口的模板,能解决大多数的滑动窗口问题:
public int[] findSubArray(int[] nums){ |
滑动窗口中用到了左右两个指针,它们移动的思路是:以右指针作为驱动,拖着左指针向前走。右指针每次只移动一步,而左指针在内部 while 循环中每次可能移动多步。右指针是主动前移,探索未知的新区域;左指针是被迫移动,负责寻找满足题意的区间。
模板的整体思想是:
1.定义两个指针 l 和 r 分别指向区间的开头和结尾,注意是闭区间;定义 sum 用来统计该区间内的各个字符出现次数;
2.第一重 while 循环是为了判断 r 指针的位置是否超出了数组边界;当 r 每次到了新位置,需要增加 r 指针的求和/计数;
3.第二重 while 循环是让 l 指针向右移动到 [l, r] 区间符合题意的位置;当 l 每次移动到了新位置,需要减少 l 指针的求和/计数;
4.在第二重 while 循环之后,成功找到了一个符合题意的 [l, r] 区间,题目要求最大的区间长度,因此更新 res = max(res, 当前区间的长度) 。
5.r 指针每次向右移动一步,开始探索新的区间。
6.模板中的 sum 需要根据题目意思具体去修改,本题是求和题目因此把sum 定义成整数用于求和;如果是计数题目,就需要改成字典用于计数。当左右指针发生变化的时候,都需要更新 sum 。
7.另外一个需要根据题目去修改的是内层 while 循环的判断条件,即: 区间 [l, r] 不符合题意 。对于本题而言,就是该区间内的 0 的个数 超过了 2 。
滑窗题目主要有两种类型:
1.窗口大小固定,例如为10,这时候相当于左右边界必定严格同步移动。
2.左指针l不回退类型,这类型一般是新加入nums[r]使得回退必定不符合条件,旧的nums[r]已经不符合条件,这种也可以利用滑窗的思想求解。
再来一道练习题:
LC209. 长度最小的子数组
给定一个含有n个正整数的数组和一个正整数target
。
找出该数组中满足其和≥ target
的长度最小的连续子数组〔numsl,numsl+1,...,numsr-1,numsr]
,并返回其长度。如果不存在符合条件的子数组,返回0。
示例1:
输入: target = 7, nums = [2,3,1,2,4,3] 输出:2
解释:子数组[4,3]是该条件下的长度最小的了教组。
示例2:
输入: target = 4,nums = [1,4,4] 输出:1
示例3:
输入: target = 11,nums = [1,1,1,1,1,1,1,1] 输出:0
思路:
滑动窗口:
这一题最关键的字眼"≥ target 的长度最小的 连续子数组"
这个字眼可以联想到很多东西
1.连续子数组:是连续的,因此可以与前缀和进行结合(实际上sum变量是一种对于前缀和的优化写法,目的是快速计算窗口的和)
同时,连续子数组,左右指针为边界就可以确定一个连续子数组->滑动窗口
因此这一题的的提示已经非常明确了,必定是用滑窗
2.长度最小:一般来说求最小长度这种全局最优状态,可以考虑动态规划或者一路维护
这里用dp的话,状态就是nums[i]结尾的最大长度,显然不太合适,dp[i-1]与dp[i]没有很明显的联系
那么就需要一路维护,求出以nums[r]为右边界的窗口的最小长度,r∈[0,len-1]
维护好nums[0]~nums[len-1]为右边界的合法窗口长度,就是全局的合法窗口最小值,也就是所求!!!
class Solution { |
为什么l指针可以义无反顾地移动至不符合条件的l+1?滑窗精髓所在->减少不必要的计算