剑指 Offer 59 - I. 滑动窗口的最大值


题目

给定一个数组 nums 和滑动窗口的大小 k,请找出所有滑动窗口里的最大值。

示例:

输入: nums = [1,3,-1,-3,5,3,6,7], 和 k = 3 输出: [3,3,5,5,6,7]

解释:

滑动窗口的位置 最大值


[1 3 -1] -3 5 3 6 7 ||||3
1 [3 -1 -3] 5 3 6 7 ||||3
1 3 [-1 -3 5] 3 6 7 ||||5
1 3 -1 [-3 5 3] 6 7 ||||5
1 3 -1 -3 [5 3 6] 7 ||||6
1 3 -1 -3 5 [3 6 7] ||||7

提示:

你可以假设 k 总是有效的,在输入数组不为空的情况下,1 ≤ k ≤ 输入数的大小。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/hua-dong-chuang-kou-de-zui-da-zhi-lcof


一、标记下标方法

定义
oldMax,oldMaxIndex,分别用来存储老的最大值和老的最大值下标,

  • 通过遍历去寻找新的max,当max与oldMax不用,则令oldMax=max。
  • 通过窗口右边界 r-oldMaxIndex是否 < k 来判断旧的最大值是否在窗口内:
  • 当oldMax在窗口内,那么只需要执行一次判断(oldmax,nums[r])的大小,复杂度为O(1);
  • 当oldMax不在窗口内。就遍历一次窗口内的值找出最大值并重新赋值给oldMax。

关键思想就上面这几条。

通过定义index自增为res[]数组赋值

代码写的比较直白,耐心看很好懂,如下:

public int[] maxSlidingWindow(int[] nums, int k) {
        if(nums==null||nums.length==0) return nums;
        int[] res = new int[nums.length-k+1];int index =0;
        int oldMax=Integer.MIN_VALUE;
        int oldMaxIndex = 0;
        int max = 0;

        for(int i=0;i<k;i++){
            max = Math.max(oldMax,nums[i]);
            if(max!=oldMax){
                oldMax = max;
                oldMaxIndex = i;
            }
        }
        res[index++]=max;
        int r = k;
        while(r<nums.length){
            //oldMaxIndex在滑动窗口中,那么只需要比较(max,nums[r]);
            if(r-oldMaxIndex<k){
                max = Math.max(oldMax,nums[r]);
                if(max!=oldMax){
                    oldMax=max;
                    oldMaxIndex=r;
                }
            }else{
                oldMax=Integer.MIN_VALUE;
                for(int i = r-k+1;i<=r;i++){
                    max = Math.max(oldMax,nums[i]);
                    if(max!=oldMax){
                        oldMax=max;
                        oldMaxIndex=i;
                    }
                }   
            }
            res[index++]=max;              
            r++;
        }
        return res;
    }

在这里插入图片描述

二、单调队列

这题要求用队列完成,我也莫得办法,也不敢确定面试时面试官喜欢第一种方法还是这种,学习路途漫漫啊~

画了一张滑动窗口的图,仔细看图:
在这里插入图片描述
这标题既然写着队列,再联想到这图,滑动窗口的下一步就是5进来,3出来,这像什么?
嗯哼~,先进先出 -> 队列麻

那么我们再看一个队列:
[1,3,2,5]
在这个队列中取最大值,当5插入进来后,根据先进先出的特性,无论前面的[1,3,2]哪个出队都不会影响5是队列中的最大值,因为[1,3,2]总是在5之前出队。

根据这个思路,我们可以定义一个单调递减队列
当插入一个数时,有两种情况如队列:[4,3,3]

  1. 当待插入的数为5时,依次对比队列中尾部的数,当尾部的数(例如 3 )比 5 小,那么将 3 移除。
  2. 当带插入的数为2时,依次对比队列中尾部的数,当尾部的数比 2 大,那么将2插入到队列尾部

  • 上面的过程保证了只要在元素 value 被插入之前队列递减,那么在 value 被插入之后队列依然递减。
  • 而队列的初始状态(空队列)符合单调递减的定义。
  • 由数学归纳法可知队列将会始终保持单调递减。

另:队列仅包含窗口内的元素,当窗口最左边元素因窗口右移而被移出,又当这个数正好是队列中的值时该怎么办呢? 咱们可以用Deque.peekFirst()==(被移出的数),是则remove,否则不用管。

public int[] maxSlidingWindow(int[] nums, int k) {
        if(nums==null||nums.length==0) return nums;
        //因为队列双端都得操做,所以用的双端队列
        Deque<Integer> eq = new LinkedList<>();
        //储存最大值
        int[] res = new int[nums.length-k+1];
        //i与res下标挂钩,初始为1-k,i++,当i为0时,刚好滑动窗口的大小为k
        for(int j=0,i=1-k;j<nums.length;i++,j++){
            //窗口右移,删除相应的元素
            if(i>0&&eq.peekFirst()==nums[i-1]){
                eq.removeFirst();
            }
            while(!eq.isEmpty()&&eq.peekLast()<nums[j])
                eq.pollLast();
            eq.offerLast(nums[j]);
            if(i>=0){
                res[i] = eq.peekFirst();
            }
        }
        return res;
    }

在这里插入图片描述
队列解法参考力扣用户Krahets解析
https://leetcode-cn.com/problems/hua-dong-chuang-kou-de-zui-da-zhi-lcof/solution/mian-shi-ti-59-i-hua-dong-chuang-kou-de-zui-da-1-6/


侵删

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值