239.滑动窗口最大值

https://leetcode-cn.com/problems/sliding-window-maximum/

思路:一次遍历,每次快速找出窗口最大值,关键在于快速,此时不能再考虑遍历窗口的方法

使用单调队列,队尾插入队首出,保持队首最大,每次窗口移动时更新队列(新元素入队,旧元素出队),则当前窗口最大值就是此时队首元素

更新队列包括插入和删除。1)插入时将nums[i]与队尾元素比较,若nums[i] > 队尾,则弹出队尾,直到队列有合适nums[i]的位置为止(为什么不用插入?因为插入涉及元素的挪动,而我们只关心队首是什么,因此无关紧要的元素直接丢弃)

2)删除:因为只考虑队首,所以只有当删除的元素等于队首时才需要操作,将队首弹出

因为需要头插和尾插,数据结构考虑使用java的LinkedList

class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {
        MonotonicQueue window = new MonotonicQueue();
        List<Integer> res = new ArrayList<>();
        for (int i = 0; i < nums.length; i++) {
            if (i < k-1) {
                //先填满窗口的前 k - 1
                window.push(nums[i]);
            } else {
                // 窗口右边界右移,加入新数字
                window.push(nums[i]);
                // 记录此时窗口最大值
                res.add(window.max());
                // 窗口左边界左移,删除旧数字
                window.pop(nums[i-k+1]);
            }
        }
        // 结果转换为数组
        int [] arr = new int [res.size()];
        for (int i = 0; i < res.size(); i++) {
            arr[i] = res.get(i);
        }
        return arr;
    }
}
// 单调队列数据结构
class MonotonicQueue {
    // 双链表,支持头部和尾部增删元素
    private LinkedList<Integer> q = new LinkedList<>();
    // 操作1:向队列加入一个元素
    public void push(int n) {
        // 将前面小于自己的元素都删除
        while (!q.isEmpty() && q.getLast() < n) {
            q.pollLast();  // 移除队尾元素
        }
        q.addLast(n);  // 加入队尾
    }
    // 操作2:返回队列中最大值
    public int max() {
        // 队头的元素肯定是最大的
        return q.getFirst();
    }
    // 操作1:删除队列中指定元素
    public void pop(int n) {
        if (n == q.getFirst()) {
            q.pollFirst();
        }
    }
}

最后更新于