【算法】滑动窗口问题

2 篇文章 0 订阅
2 篇文章 0 订阅

问题

有一个整型数组arr和一个大小为w的窗口从数组的最左边划到最右边,窗口每次向右滑动一个位置
如数组为[4,3,5,4,3,3,6,7],窗口大小为3时
[【4,3,5】,4,3,3,6,7]最大值是5
[4,【3,5,4】,3,3,6,7]最大值是5
[4,3,【5,4,3】,3,6,7]最大值是5
以此类推。。。。
数组长度为n窗口大小为w,则一共产生n-w+1个窗口最大值

输入:整型数组arr,窗口大小w
输出:一个长度为n-w+1的数组res,res[i]表示每种窗口状态下的最大值,上例应返回[5, 8, 8]。

思路分析

可以先把初始窗口的最大值记录下来,每次向右移动一个位置,就看看最大的元素有没有被移除如果被移除了,则需要能找到第二大的元素,因此不能仅仅保存最大的数,用一个队列来保存,从头到尾按大小排列,新加入的元素需要按照大小给一个排位(事实上,原本队列中的比新元素小的元素可以被舍弃,因为他们本来就比新元素前,比新元素先排除,不会再新元素移除后成为次大的元素)。

那么队列应该保存什么数据呢,可以有两种:

1:保存具体最大的数值,如果移出的元素等于队头的元素,就把队头元素排除(会不会出现这种情况:队列中有小于当前队头,但是在当前队头前面的值,在这个值排出时,因为他不是队头,没有被比较排除,然后在后面成为队头?不会,队列中其他元素不会有比队头前的值,根据上面的条件,现在队头的这个值在入队时就把队列中小于他的值舍弃掉了,队列中比当前队头大的值又会在前面被排除)

2:保存最大的数值的下标,如果队头元素位置已不在窗口则把队头排除

这道题我们利用双端队列来实现最大值的关心,首先生成双端队列qmax,qmax中存放的是数组arr的下标。
同时我们制定如下的规则:
假设遍历到arr[i],qmax的放入规则为:
1:如果qmax为空,直接把下标i放进qmax,放入过程结束。
2:如果qmax不为空,取出当前qmax队尾存放的下标,假设为j。
1):如果arr[j]>arr[i],直接把下标i放入qmax的队尾,放入过程结束。
2):如果arr[j]<=arr[i],把j从qmax中弹出,重复qmax的放入规则。
也就是说,如果qmax是空的,就直接放入当前位置,如果qmax不是空的,那么就判断qmax的对头元素中对应的arr元素是否比当前值大,如果不是,那么我们直接淘汰队头,因为队头元素就是最老的最前面的本身就是最先要被淘汰的数据。而如果不是,那么我们把刚才的新位置放入到qmax的队尾。
然后假设遍历到了arr[i],qmax的弹出规则为:
如果qmax对头的下标等于i-w,说明当前qmax对头的下标已经过期,弹出当前对头的下标即可。
比如,当前队中元素为0,1,2(都是数组的下标),然后此时i=3,w=3,那么此时继续向下遍历数组的时候,就需要出队了,那么就是把0出队。

那么我们简单的推演一下上面的题目的过程:
1:开始时候qmax为空,qmax={}
2:遍历到arr[0]=4,将下标0放入qmax,qmax={0}
3:遍历到arr[1]=3,当前qmax的队列下标为0,且有arr[0]>arr[1],所以下标1直接放入,qmax={0,1}
4:遍历到arr[2]=5,当前qmax的队尾下标为1,且arr[1]<arr[2],所以弹出下标1,同理有arr[0]<arr[2],所以弹出下标0,得到qmax={},
之后再放入下标2有qmax={2}. 此时窗口arr[0…2]出现,并且有qmax=2,窗口最大值为arr[2]
5:遍历到arr[3]=4,当前qmax的队尾下标为2,且有arr[2]>arr[3],所以将下标3放入qmax尾部,qmax={2,3},窗口arr[1…3]出现,当前队头的下标为2,这个下标没有过期,有最大值arr[2]
6:遍历到arr[4]=3,与上面同理,得到qmax={2,3,4},窗口arr[2…4]出现,qmax队头为2,所以最大为arr[2]=5
7:遍历到arr[5]=3,当前qmax队尾下标为4,且arr[4]<=arr[5],所以将下标4从qmax的尾部弹出,qmax变为{2,3},当前队尾下标为3,arr[3]>arr[5],所以直接压入,有qmax={2,3,5},窗口arr[3…5]出现,当前队头元素为2,已经过期,所以弹出,变为qmax={3,5},当前qmax下标为3,并且没有过期,所以得到最大窗口值arr[3]

接下来的过程一样,直接以此类推即可。

代码实现

package com.base.learn.stack;


import java.util.LinkedList;

/**
 * @author: 张锦标
 * @date: 2023/5/27 21:10
 * SlideWindow类
 */
public class SlideWindow {
    public static int[] getMaxWindow(int[] arr, int w) {
        if (arr == null || w < 1 || arr.length < w) {
            return null;
        }
        LinkedList<Integer> qmax = new LinkedList<>();
        int[] res = new int[arr.length - w + 1];
        int index = 0;
        for (int i = 0; i < arr.length; i++) {
            //判断qmax是否为空 不为空 那么判断当前元素是否大于队尾
            //如果大于队尾元素 那么直接移除队头元素
            if (!qmax.isEmpty() && arr[qmax.peekLast()] <= arr[i]){
                qmax.pollFirst();
            }
            //无论如何都会将当前索引插入队尾
            qmax.addLast(arr[i]);
            //当前队头元素索引是否等于i-w 如果是,说明过期
            if (qmax.peekFirst() == i - w){
                qmax.pollFirst();
            }
            //判断是否开始生成窗口
            if (i>=w-1){
                res[index++] = arr[qmax.peekFirst()];
            }
        }
        return res;
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值