单调队列详细图解-leetcode239滑动窗口最大值

 

1. 单调队列(双端队列)

       核心思想是维持deque/双端队列中的元素保持递增or递减。

       使用该数据结构的优点是deque在队列两端都可以添加、删除元素,这里借助了它其中4种常数时间复杂度的操作(java):offerLast(n)、getFirst()、pollFirst()、pollLast()。

单调队列详细图解-leetcode239滑动窗口最大值_第1张图片

单调栈介绍详见:https://blog.csdn.net/LutherK/article/details/107023543

另外:单调队列还被应用于“多重背包问题”的优化解法。

 

2. 例题

Leetcode 239题目:

输入: 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

 

3. 图解

思路:

      在一堆数字中,已知最值,如果给这堆数添加一个数,那么比较一下就可以很快算出最值;但如果减少一个数,就不一定能很快得到最值了,而要遍历所有数重新找最值。

      首先考虑,要在O(N)的时间内解决此问题,我们需要3个O(1)的操作帮助我们完成,分别是:

              push(n):在队列末尾添加元素n;

              max:取当前队列/窗口中的最大值;

              pop(n):r若队首元素是n,则删除n;

     在Java中的实现如下:

​
    // 用于"单调队列"的成员内部类
    private static class MonotonicQueue {
        Deque deque = new ArrayDeque<>();
        void push(int n){
            while(!deque.isEmpty() && deque.getLast()

单调队列详细图解-leetcode239滑动窗口最大值_第2张图片

 

4. 完整Java代码

    // (2) 双端队列(单调队列)
    public static int[] maxSlidingWindow2(int[] nums, int k) {
        int n = nums.length;
        MonotonicQueue window = new MonotonicQueue();
        int[] res = new int[n-k+1];
        for(int i=0;i deque = new ArrayDeque<>();
        void push(int n){
            while(!deque.isEmpty() && deque.getLast()

补充:

此题还有另外一种解法,时间复杂度也是O(N),可以说是DP,也可是看做“同步双向遍历”。

代码:

    // (1)动态规划
    // left[i] 表示从窗口开始到下标i的最大值
    // right[j]  表示从窗口末尾到索引j的最大值
    public static int[] maxSlidingWindow(int[] nums, int k) {
        int n = nums.length;
        if (n * k == 0) return new int[0];
        if (k == 1) return nums;

        int[] left = new int[n];
        left[0] = nums[0];
        int[] right = new int[n];
        for (int i = 1; i < n; i++) {
            if (i%k==0) left[i] = nums[i];
            else
                left[i] = Math.max(left[i-1],nums[i]);
            int j = n-1-i;
            if((j+1)%k==0)
                right[j] = nums[j];
            else
                right[j] = Math.max(right[j+1],nums[j]);
        }
        int[] res = new int[n-k+1];
        for (int i = 0; i <= n - k; i++) {
            res[i] = Math.max(right[i],left[i+k-1]);
        }
        return res;
    }

纯属个人见解,如有错误望批评指正!

 

 

你可能感兴趣的:(数据结构与算法,leetcode,数据结构,leetcode,算法,java)