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

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

注意:本题与主站 239 题相同:https://leetcode-cn.com/problems/sliding-window-maximum/

解题思路

最暴力的做法当然是模拟滑动窗口的过程,扫描每个滑动窗口中的所有数字并找出其中的最大值,这样的复杂度是 O ( k n ) O(kn) O(kn)的。显然题目出在这里肯定不是要我们用暴力解法。

窗口向右滑动的过程实际上就是将处于窗口的第一个数字删除,同时在窗口的末尾添加一个新的数字,这就可以用双向队列来模拟,每次把头部的数字弹出,再把新的数字添加到队列尾部,然后找队列中最大的元素即可。
在这里插入图片描述

为了使用 O ( 1 ) O(1) O(1)的时间就能找到最大的元素,我们并不把滑动窗口的每个数值都存入队列,而是只把有可能成为滑动窗口最大值的数值存入队列。

  1. 当前窗口中元素大于k个时,说明队头的元素已经从窗口滑出,需要从队列头部出队
  2. 队尾已有的数字小于等于当前要存入的数字,那么队尾这些数字已经不可能是滑动窗口的最大值,因为待存入的数字比它们大,还要比他们晚滑出窗口。所以队尾这些元素都要从队尾出队
  3. 当前面元素都滑出当前窗口时,当前元素可能会是后面窗口的最大值,所以当前元素无论如何都要入队。

因此我们维护一个双向队列,队列放的是元素的下标。我们维护该双端队列的队头是整个队列的最大元素所在下标

时间复杂度: 每个元素最多入队出队一次,复杂度为 O ( n ) O(n) O(n)

Java代码

class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {
        if(nums == null || nums.length == 0) return new int[0];
        Deque<Integer> deque = new LinkedList<>();
        int[] res = new int[nums.length - k + 1];
        
        int index = 0;
        for(int i = 0;i < nums.length;i++){
            if(!deque.isEmpty() && i - deque.peekFirst() + 1 > k){//当前窗口中元素大于k个
                    deque.removeFirst();//队头出队
            }
            //当前元素大于队尾元素,则队尾这些元素只要当前元素在,就不可能是窗口中的最大值,可以直接拿走了
            while(!deque.isEmpty() && nums[i] >= nums[deque.peekLast()]){
                deque.removeLast();
            }
            //当前面元素都滑出当前窗口时,当前元素可能会是后面窗口的最大值,所以当前元素无论如何都要入队
            //注意队列中存的是元素在数组中的下标,方便判断窗口中当前元素有多少个了
            deque.addLast(i);
            if(i + 1 >= k){//窗口中元素有k个时
                res[index++] = nums[deque.peekFirst()];
            }
        }
        return res;
    }
}

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值