单调队列-滑动窗口最大值

98 阅读2分钟

单调队列-滑动窗口最大值

介绍

  单调队列的经典用法:维持滑动窗口在滑动中的最大值(从队尾到队头单调递增)。队列中的元素随着滑动窗口的移动都有成为最大值的可能性,所以需要该队列暂时保存。

单调递增队列实现代码

public static class MyQueue {
    public ArrayDeque<Integer> deque = new ArrayDeque<>();
    private int[] nums;

    public MyQueue(int[] nums) {
        this.nums = nums;
    }

    public Integer poll(int index) {//滑动窗口左边界
        if (deque.isEmpty() || deque.peekFirst() != index) {//过期判断,没过期
            return -1;
        }
        return deque.pollFirst();//过期了
    }

    public Integer max() {
        return deque.peekFirst();
    }

    public void offer(int ele) {
        while (!deque.isEmpty() && nums[deque.peekLast()] <= nums[ele]) {
            deque.pollLast();
        }
        deque.offerLast(ele);
    }
}

题目:滑动窗口最大值

描述 239. 滑动窗口最大值

  给你一个整数数组 nums,有一个大小为 k **的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。

返回 滑动窗口中的最大值

分析

  1. 看到这里的初始想法,可以使用优先队列来完成,随着窗口的滑动,可以先移除左窗口的元素,再把右窗口的元素加入到优先队列中。该做法放到leetcode上超时了。优先队列移除一个元素和加入一个元素的代价都是O(logN)级别的。总体就是O(NlogN)。
  2. 正确解法:单调队列。在滑动过程中,维持一个单调递增队列,每次取队列头部元素即为当前窗口的最大值。总体代价为O(N)。

代码实现

public int[] maxSlidingWindow(int[] nums, int k) {

    MyQueue maxQueue = new MyQueue(nums);
    for (int i = 0; i < k - 1; i++) {//入队前k-1个元素
        maxQueue.offer(i);
    }
    int resSize = nums.length - k + 1;
    int[] res = new int[resSize];//结果记录
    int left = 0;
    int right = k;//左闭右开
    for (int i = 0; i < resSize; i++) {
        maxQueue.offer((right - 1));//加入队列
        right++;
        res[i] = nums[maxQueue.max()];
        maxQueue.poll(left++);//移除左窗口
    }
    return res;
}

总结

  单调队列的经典用法,用于获取滑动窗口在滑动过程中的最大值。