楔子:我发现编造一些中二的东西倒不如直接写小说,像我这种正派又帅气的程序员,还想讲道理还想写小说确实不太靠谱,所以现在开始我决定说人话了!我的系列文章,算法类别的都希望跟大家讲一个通用思路+实例的形式,授人以鱼不如授人以渔嘛,至于通用技术之类的可能就直接讲内容了,大家有什么想问的想学的可以给我留言,只要是能讲的必定知无不言,我会作为后期的素材内容。
什么是滑动窗口?为什么要讲滑动窗口?
KMP算法是应用了滑动窗口最典型的例子,但此片文章不以KMP算法来做讲解例子,因为这个算法里面已经容纳了很多前人的处理与思想,难懂的同时可能自带劝退属性。数组是一种基本的数据结构,会写代码的人不会数组操作都不敢出门见人,但是很多基于数据的操作会浪费大量的时间,滑动窗口就是为了帮助我们提升效率的一种算法。
我们就从lintCode上找一个最简单直白的滑动窗口算法来进行距离,究竟该怎么分析滑动窗口算法,后面还会附上几道进阶滑窗,有兴趣的同学可以看一下:
什么时候使用滑动窗口算法?
之所以说这道题简单,是因为他刚上来就告诉你了,这个要使用滑动窗口。那么什么情况下使用滑窗呢?首先滑窗是为了解决效率问题的,也就是说,滑窗可以解决的问题,一般数组都可以解决,所以当我们开始解决一个问题的时候,涉及到多维数组操作(简单来说就是时间复杂度为
所以该算法的时间复杂度为
滑窗算法的一般步骤
这里我总结了几个滑窗算法,找了一个解决滑窗算法的一般套路:
1、选择初始窗口
这一步一般不难,滑窗算法一般都有个start和end,我们只要确定自己的start和end就好了,这个问题已经给出了,start肯定就是0,end肯定就是0+k。
比较难一些的滑窗算法,窗口大小是会改变的,kmp的滑窗为字串长度。
2、确认滑动策略
这一步就是让我们确认什么时候要开始改变滑窗的状态,对于这个问题,我们知道滑窗状态有:1、最大值的index;2、初始位置;3、终止位置。对于我们这道题来讲,滑窗状态改变是很直白的:
1、当滑窗右边出现一个值比当前最大值还大时,改变最大值index
2、当滑窗左边大于最大值index时,改变最大值index
3、每一次滑动改变窗口位置
对于kmp也有类似的策略: 当匹配失败时,将窗口向后移动n个位数,使得窗口的第i个位置与母串对齐,此时,i前面的字符串应该与之前匹配的一致(这之后就要引用公共前后缀的概念了,所以很多老师上来就讲前后缀会让初学者一脸懵逼)
3、确认输出策略
我们解决任何问题的时候,都不要忘了自己当时是为了解决什么问题。搞复杂算法搞着搞着我们会懵掉大多数原因是忘了初心,只为了用算法而用算法,这和很多公司是一样的,为了加班而加班,为了用新技术而用新技术,为了狼性而狼性。对于这道题,我们是要输出最大值,而弹窗又是每次循环都会移动的,所以我们每次移动输出一次当前largeIndex的值
第一步:
第二步:
第三步:此时发现新增的大于之前的,改变largeIndex
具体代码如下:
public List<Integer> maxSlidingWindow(int[] nums, int k) {
List<Integer> ans = new ArrayList<Integer>(){};
int start = 0;
int end = k-1;
int largeIndex = 0;
//不够一个窗口
if(nums.length < k || nums.length == 0){
return ans;
}
//寻找第一个窗口最大值,初始化
for(int i = 0; i < k ; i++){
if(nums[i] > nums[largeIndex]){
largeIndex = i;
}
}
//开始滑动,以窗口右侧为主视角
for(int i = end; i< nums.length;i++){
if(nums[i] > nums[largeIndex]){
largeIndex = i;
}
if(start > largeIndex){
largeIndex = start;
for(int j = start; j <= end;j++){
if(nums[j] > nums[largeIndex]){
largeIndex = j;
}
}
}
ans.add(nums[largeIndex]);
start++;
end++;
}
return ans;
}
对于kmp算法,当窗口内所有字符都匹配成功时,输出
到这里就结束了,除了此题,和KMP算法,再给各位提供一道题进行练手吧:
问题描述:
给定一个01数组arr
和 一个整数k
, 统计有多少区间符合如下条件:
1、区间的两个端点都为 0 (允许区间长度为1)
2、区间内 1 的个数不多于k
样例
样例 1:
输入: arr = [0, 0, 1, 0, 1, 1, 0], k = 1 输出: 7 解释: [0, 0], [1, 1], [3, 3], [6, 6], [0, 1], [0, 3], [1, 3] (区间 [i,j] 表示下标 i(包括)和下标 j(包括)之间的元素)
样例 2:
输入: arr = [1, 1, 1, 0, 0, 1], k = 2 输出: 3 解释: [3, 3], [4, 4], [3, 4] (区间 [i,j] 表示下标 i(包括)和下标 j(包括)之间的元素) 注意事项arr
的大小不超过10^5
写在最后:算法题目本身可能除了笔试面试,不会为我们带来什么好处。但是算法的思路会经常用在我们的工程项目中或者日常生活中,算法给我们带来的逻辑思维能力也是我们贫瘠生活里的补品。最后祝各位武运昌隆,祝祖国繁荣昌盛,祝疫情早点过去~