Skip to content

队列

2021-03-05

简介

队列是一种「先进先出」的数据结构。元素从队列的前端进入(入队),从末端离开(出队),类似于排队。

width=360px

双向队列

双向队列支持从两端插入或删除元素。

单调队列

单调队列的元素从队头到队尾满足单调性,适用于查询某一动态区间的最大(或最小)元素。

单调队列一般使用 双向队列 实现。

插入元素

A[i] 入队时,需要维护队列单调性,同时保证队列元素在 A[pi] 范围内。

以单调递增队列为例,维护操作如下:

  1. 重复弹出队头,直到队头 p
  2. 重复弹出队尾,直到 A[队尾]<A[i]
  3. i 入队。

此时 A[pi] 范围内最小元素为 A[队头]

TIP

这里的单调队列中,存储的是下标值。队列内元素按 A 的值单调递增。

cpp
deque<int> q; // 存储元素下标

void insert(int i, int p) { // 将 a[i] 入队,维护队列元素在 a[p...i] 范围内
    while (!q.empty() && q.front() < p)
        q.pop_front();
    while (!q.empty() && a[q.back()] >= a[i])
        q.pop_back();
    q.push_back(i);
}

滑动窗口

一个滑动窗口(长度为 k)从数组 A (长度为 n)的左端移动到右端,每次只向右移一位。求每次滑动时窗口区中的最大值。

示例(k=3,n=8,红色数值在窗口区内):

A1A2A3A4A5A6A7A8最大值
-353673
153673
133675
13-1675
13-1-376
13-1-357

朴素算法

枚举 i=kn,顺序地查找 A[ik+1i] 中的最大整数。

时间复杂度为 O(nk)

cpp
for (int i = k; i <= n; i ++) {
    int maxn = a[i];
    for (int j = i - k + 1; j <= i; j ++)
        maxn = max(maxn, a[j]);
    cout << maxn << ' ';
}

单调队列优化

使用单调递减队列优化「查找 A[ik+1i] 中的最大整数」的效率。

时间复杂度为 O(n)

cpp
deque<int> q;

void insert(int i, int p) {
    while (!q.empty() && q.front() < p)
        q.pop_front();
    while (!q.empty() && a[q.back()] <= a[i])
        q.pop_back();
    q.push_back(i);
}

for (int i = 1; i <= n; i ++) {
    insert(i, i - k + 1);
    if (i >= k)
        cout << a[q.front()] << ' ';
}