362. 滑动窗口的最大值

描述

给出一个可能包含重复的整数数组,和一个大小为 k 的滑动窗口, 从左到右在数组中滑动这个窗口,找到数组中每个窗口内的最大值。

样例

给出数组[1,2,7,7,8], 滑动窗口大小为k = 3. 返回[7,7,8].
解释:
最开始,窗口的状态如下:
[|1, 2 ,7| ,7 , 8], 最大值为7;
然后窗口向右移动一位:
[1, |2, 7, 7|, 8], 最大值为7;
最后窗口再向右移动一位:
[1, 2, |7, 7, 8|], 最大值为8.

挑战

O(n)时间,O(k)的额外空间

思路

  1. 暴力两层 for 循环,O(nk)
  2. 堆 + for 循环,O(nlogk)
  3. 双端队列,O(n)

思路

将 dequeue 做成一个单调下降序列,某一个要添加到 dequeue 的数如果比 dequeue 中最后一个数小,则该数如果想成为滑动窗口的最大值,需要等 dequeue 中比该数大的这个末尾数被抛出,该数才有机会。同理,如果向 dequeue 加入一个更大值,则 dequeue 内部位于该数前面且小于该数的所有值都不会再有机会成为滑动窗口最大值,全部删掉,也就是说,加入某一元素时从 dequeue 末尾开始删除dequeue中所有比其小的元素,dequeue 从 first 端删除的值是上一个最大值,加入结果数组

代码

public class Solution {    
    /**
     * @param nums: A list of integers.
     * @return: The maximum number inside the window at each moving.
     */
    // 在加入新的元素前要将队列中所有小于当前欲加入元素的元素都删掉
    void inQueue(Deque deque, int num) {
        while (!deque.isEmpty() && deque.peekLast() < num) {
            deque.pollLast();
        }
        deque.offer(num);
    }
    
    // 因为新的元素在加入 deque 时会同时删除 deque 中比它小的元素
    // 所以可能 nums[i - k + 1] 压根就没存储在 deque 中
    // 在删除时需要先判断下当前 deque 的第一个元素是否等于nums[i - k + 1]
    void outQueue(Deque deque, int num) {
        if (deque.peekFirst() == num) {
            deque.pollFirst();
        }
    }
    
    // 使双端队列保持递减序,队列中的第一个元素是最大值
    public ArrayList maxSlidingWindow(int[] nums, int k) {
        ArrayList ans = new ArrayList();
        Deque deque = new ArrayDeque();
        if (nums.length == 0) {
            return ans;
        }
        for (int i = 0; i < k - 1; i++) {
            inQueue(deque, nums[i]);
        }
        
        /* deque每加入一个元素后要执行一次删除操作
         * 来保证当队列第一个元素不在滑动窗口范围时会被移除队列,
         * 如果未不移除就会出现尽管窗口在不断移动
         * 但输出的最大值始终就是那么一个数
         */
        for(int i = k - 1; i < nums.length; i++) {
            inQueue(deque, nums[i]);
            ans.add(deque.peekFirst());
            outQueue(deque, nums[i - k + 1]);
        }
        return ans;
    }
}

你可能感兴趣的:(362. 滑动窗口的最大值)