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 ≤ 输入数组的大小。

思路-单调非递增+滑动窗口

遍历nums数组,每轮保证单调队列deque:

  1. deque内仅包含窗口内的元素
  2. deque内的元素非严格递减
  3. 队首元素就是窗口的最大元素
  • nums为null或0,返回空数组(new int[0])
  • 定义一个栈和一个结果数组(大小nums.length-k+1)
  • 构造单调队列1的首个窗口(单调非递增)
    1. 队列不为空时,当前值与队列尾部值比较,如果大于,删除队列尾部值, 一直循环删除到队列中的值都大于当前值,或者删到队列为空
    2.执行完上面的循环后,队列中要么为空,要么值都比当前值大
  • 窗口形成后,就需要把队列首位添加到数组中,因为下面的循环是直接跳过这一步的,所以需要我们直接添加
  • 循环依次构造窗口
    1. 构造下一个窗口需要删除上一个窗口的首元素如果首位等于nums[i-k],那么说明此时nums[i-k]就是上一个窗口的首元素值,而下一个窗口不需要它,需要删除
    2. 队列不为空时,当前值与队列尾部值比较,如果大于,删除队列尾部值, 一直循环删除到队列中的值都大于当前值,或者删到队列为空
    3. 执行完上面的循环后,队列中要么为空,要么值都比当前值大
    4. 单调非递增队首元素就是最大值
  • 返回结果集

代码

class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {
        if(nums==null||nums.length==0){
            return new int[0];
        }
        //单调非递增栈
        Deque<Integer> stack=new LinkedList<>();
        //结果集大小nums.length-k+1
        int[] res=new int[nums.length-k+1];
        //未成形窗口
        for(int i=0;i<k;i++){
            //队列不为空时,当前值与队列尾部值比较,如果大于,删除队列尾部值
            //一直循环删除到队列中的值都大于当前值,或者删到队列为空
            while(!stack.isEmpty()&&stack.peekLast()<nums[i]){
                stack.removeLast();
            }
             //执行完上面的循环后,队列中要么为空,要么值都比当前值大,然后就把当前值添加到队列中
            stack.addLast(nums[i]);
        }
        //因为窗口形成后,就需要把队列首位添加到数组中,而下面的循环是直接跳过这一步的,所以需要我们直接添加
        res[0]=stack.peekFirst();
        //形成窗口
        for(int i=k;i<nums.length;i++){
            //构造下一个窗口需要删除上一个窗口的首元素
            //i-k是已经在区间外了,如果首位等于nums[i-k],那么说明此时首位值是上一个窗口的值,需要删除
            if(stack.peekFirst()==nums[i-k]){
                stack.removeFirst();
            } 
            //队列不为空时,当前值与队列尾部值比较,如果大于,删除队列尾部值
            //一直循环删除到队列中的值都大于当前值,或者删到队列为空
            while(!stack.isEmpty()&&stack.peekLast()<nums[i]){
                stack.removeLast();
            }
            //执行完上面的循环后,队列中要么为空,要么值都比当前值大,然后就把当前值添加到队列中
            stack.addLast(nums[i]);

            //单调非递增栈首元素就是最大值
            res[i-k+1]=stack.peekFirst();

        }
        return res;
    }
}

优化版

class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {
        if(nums.length == 0 || k == 0) return new int[0];
        Deque<Integer> deque = new LinkedList<>();
        int[] res = new int[nums.length - k + 1];
        for(int j = 0, i = 1 - k; j < nums.length; i++, j++) {
            // 删除 deque 中对应的 nums[i-1]
            if(i > 0 && deque.peekFirst() == nums[i - 1])
                deque.removeFirst();
            // 保持 deque 递减
            while(!deque.isEmpty() && deque.peekLast() < nums[j])
                deque.removeLast();
            deque.addLast(nums[j]);
            // 记录窗口最大值
            if(i >= 0)
                res[i] = deque.peekFirst();
        }
        return res;
    }
}

复杂度

时间复杂度O(n): 其中 nn 为数组 numsnums 长度;线性遍历 numsnums 占用 O(n)O(n) ;每个元素最多仅入队和出队一次,因此单调队列 dequedeque 占用 O(2n)O(2n) 。
空间复杂度 O(k) : 双端队列 dequedeque 中最多同时存储 k 个元素(即窗口大小)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值