怎么改变窗口大小_谈一谈“滑动窗口”

13653553b3ab0f8aa82406d61dcb4661.png

楔子:我发现编造一些中二的东西倒不如直接写小说,像我这种正派又帅气的程序员,还想讲道理还想写小说确实不太靠谱,所以现在开始我决定说人话了!我的系列文章,算法类别的都希望跟大家讲一个通用思路+实例的形式,授人以鱼不如授人以渔嘛,至于通用技术之类的可能就直接讲内容了,大家有什么想问的想学的可以给我留言,只要是能讲的必定知无不言,我会作为后期的素材内容。

什么是滑动窗口?为什么要讲滑动窗口?

e298d5d2f419dbc520e75a7582b13c48.png

KMP算法是应用了滑动窗口最典型的例子,但此片文章不以KMP算法来做讲解例子,因为这个算法里面已经容纳了很多前人的处理与思想,难懂的同时可能自带劝退属性。数组是一种基本的数据结构,会写代码的人不会数组操作都不敢出门见人,但是很多基于数据的操作会浪费大量的时间,滑动窗口就是为了帮助我们提升效率的一种算法。

我们就从lintCode上找一个最简单直白的滑动窗口算法来进行距离,究竟该怎么分析滑动窗口算法,后面还会附上几道进阶滑窗,有兴趣的同学可以看一下:

ccfee11a67cb32db836c7a3e32004a14.png

什么时候使用滑动窗口算法?

之所以说这道题简单,是因为他刚上来就告诉你了,这个要使用滑动窗口。那么什么情况下使用滑窗呢?首先滑窗是为了解决效率问题的,也就是说,滑窗可以解决的问题,一般数组都可以解决,所以当我们开始解决一个问题的时候,涉及到多维数组操作(简单来说就是时间复杂度为

,对于这道题,我们知道最简单的方法就是对于每一个窗口的第一个,都以它为起点遍历整个窗口的长度,然后寻找最大值,那么这种做法的时间复杂度为:

时,该值为

所以该算法的时间复杂度为

,那么如何减少时间复杂度呢?我们通用的想法就是剪枝优化,这里我们发现其实没必要每次都比较,当窗口滑动的时候,我们完全可以利用之前窗口的结果(
利用之前计算的结果辅助下次计算是很好的编程思路,动态规划也有使用)来计算这次的结果。但是请各位注意,滑窗算法也有他的局限性,当数组数据具有某种规律时,滑窗算法不一定优于传统算法。

滑窗算法的一般步骤

这里我总结了几个滑窗算法,找了一个解决滑窗算法的一般套路:

1、选择初始窗口

这一步一般不难,滑窗算法一般都有个start和end,我们只要确定自己的start和end就好了,这个问题已经给出了,start肯定就是0,end肯定就是0+k。

比较难一些的滑窗算法,窗口大小是会改变的,kmp的滑窗为字串长度。

e1f982c9495de7b48bb9a2f63bc7b7c8.png

2、确认滑动策略

这一步就是让我们确认什么时候要开始改变滑窗的状态,对于这个问题,我们知道滑窗状态有:1、最大值的index;2、初始位置;3、终止位置。对于我们这道题来讲,滑窗状态改变是很直白的:

1、当滑窗右边出现一个值比当前最大值还大时,改变最大值index

2、当滑窗左边大于最大值index时,改变最大值index

3、每一次滑动改变窗口位置

对于kmp也有类似的策略: 当匹配失败时,将窗口向后移动n个位数,使得窗口的第i个位置与母串对齐,此时,i前面的字符串应该与之前匹配的一致(这之后就要引用公共前后缀的概念了,所以很多老师上来就讲前后缀会让初学者一脸懵逼)

3、确认输出策略

我们解决任何问题的时候,都不要忘了自己当时是为了解决什么问题。搞复杂算法搞着搞着我们会懵掉大多数原因是忘了初心,只为了用算法而用算法,这和很多公司是一样的,为了加班而加班,为了用新技术而用新技术,为了狼性而狼性。对于这道题,我们是要输出最大值,而弹窗又是每次循环都会移动的,所以我们每次移动输出一次当前largeIndex的值

第一步:

e8e87e6feb41172cc2eda53caaff4769.png

第二步:

cece2981f15cea235d908ce13a1b6403.png

第三步:此时发现新增的大于之前的,改变largeIndex

615070bcedbd7b74c0a638e3fbd27e51.png

具体代码如下:

 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

写在最后:算法题目本身可能除了笔试面试,不会为我们带来什么好处。但是算法的思路会经常用在我们的工程项目中或者日常生活中,算法给我们带来的逻辑思维能力也是我们贫瘠生活里的补品。最后祝各位武运昌隆,祝祖国繁荣昌盛,祝疫情早点过去~

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值