单调队列常见模型:找出滑动窗口中的最大值/最小值。模板:

  1. q = deque()
  2. for i in range(n):
  3. # 判断队头是否滑出窗口
  4. while q and checkout_out(q[0]):
  5. q.popleft()
  6. while q and check(q[-1]):
  7. q.pop()
  8. q.append(i)

在队列中,这些下标按照从小到大的顺序被存储,并且它们在数组 nums 中对应的值是严格单调递减的。

239. 滑动窗口最大值

滑动窗口的位置                  queue            最大值           
---------------               -----                        -------
[1  3  -1] -3  5  3  6  7     [ 1, 2 ]          3             
 1 [3  -1  -3] 5  3  6  7     [ 1, 2, 3 ]       3                            
 1  3 [-1  -3  5] 3  6  7     [ 4 ]             5
 1  3  -1 [-3  5  3] 6  7     [ 4, 5 ]          5
 1  3  -1  -3 [5  3  6] 7     [ 6 ]             6
 1  3  -1  -3  5 [3  6  7]    [ 7 ]             7

当滑动窗口向右移动时,我们需要把一个新的元素放入队列中。为了保持队列的性质,我们会不断地将新的元素与队尾的元素相比较,

  • 如果前者大于等于后者,那么队尾的元素就可以被永久地移除,我们将其弹出队列。

我们需要不断地进行此项操作,直到队列为空或者新的元素小于队尾的元素。

function maxSlidingWindow(nums: number[], k: number): number[] {
    let queue = [];
    for (let i = 0; i < k; i++) {
        while (queue.length && nums[i] >= nums[queue[queue.length - 1]]) {
            queue.pop();
        }
        queue.push(i);
    }
    let ans = [nums[queue[0]]];
    for (let i = k; i < nums.length; i++) {
        while (queue.length && nums[i] >= nums[queue[queue.length - 1]]) {
            queue.pop();
        }
        queue.push(i);
        while (queue[0] <= i - k) {
            queue.shift();
        }
        ans.push(nums[queue[0]]);
    }
    return ans;
};