滑动窗口的最大值[单调双端队列]

311 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第6天,滑动窗口的最大值[单调双端队列] - 掘金 (juejin.cn)

前言

对于有序/连续子数组的最大最小问题,单调栈/队列/双端队列都是很好的记录小能手,记住这些大小值问题,就能降低时间复杂度,以空间换时间。

一、滑动窗口的最大值

image.png

二、单调双端队列ArrayDeque

A.idea

target:给定一个固定大小的窗口,从数组左边往右滑动,取每个窗口中的最大值。

M1:维持一个非严格单调递减队列,滑动一格,会有一个新元素和旧元素。

1-添加新元素,如果该元素比队尾小,直接入队,否则从队尾出队元素,直至队列为空或者队尾元素大于新元素;

2-移除旧元素,如果该元素比队首小,直接不管,否则从队首出队元素;

3-取每个窗口最大值,取队首值即可,它是单调队列中最大的值。

B.code

package everyday;

import java.util.ArrayDeque;

// 滑动窗口的最大值。
public class MaxSlidingWindow {
    /*
    target:给定一个固定大小的窗口,从数组左边往右滑动,取每个窗口中的最大值。
    M1:维持一个非严格单调递减队列,滑动一格,会有一个新元素和旧元素。
    添加新元素,如果该元素比队尾小,直接入队,否则从队尾出队元素,直至队列为空或者队尾元素大于新元素;
    移除旧元素,如果该元素比队首小,直接不管,否则从队首出队元素;
    取每个窗口最大值,取队首值即可,它是单调队列中最大的值。
     */
    public int[] maxSlidingWindow(int[] nums, int k) {
        int n = nums.length;
        if (0 == n) return new int[]{};
        // assert k > 0 && k < nums.length;
        // 采用双端队列。
        ArrayDeque<Integer> que = new ArrayDeque<>();
        int begin = 0;
        // 先将k - 1一个元素放进去。
        while (begin < k - 1) {
            while (!que.isEmpty() && que.peekLast() < nums[begin]) que.pollLast();
            que.add(nums[begin++]);
        }
        int[] rs = new int[n - k + 1];
        // 开始取出旧元素,添加新元素,取最大值。
        for (int i = begin; i < n; i++) {
            // 取旧元素。
            int oldIdx = i - k;
            if (oldIdx >= 0 && nums[oldIdx] == que.peek()) que.poll();
            // 填新元素。
            while (!que.isEmpty() && que.peekLast() < nums[i]) que.pollLast();
            que.add(nums[i]);
            // 取最大值加入数组。
            rs[i - begin] = que.peek();
        }
        return rs;
    }
}

总结

1)单调双端队列的应用场景与练习。

2)ArrayDeque双端队列。

参考文献

[1] LeetCode 滑动窗口的最大值