队列
💡 核心思想
队列是一种先进先出(FIFO)的数据结构,从队尾入队、从队首出队。在竞赛中,队列是 BFS 的核心数据结构。进阶应用包括单调队列(滑动窗口最值)和双端队列。
单调队列是 CSP-S 的常考知识点。它可以在 $O(n)$ 时间内求出滑动窗口的最大/最小值。核心思想是维护一个双端队列,保持队列中的元素对应的值单调,当窗口滑动时移除过期的元素。
🎯 直觉理解
队列就像排队买票——先来的人先买到(先进先出)。单调队列就像一个VIP通道——如果你后面来了一个比你强的人,你就可以走了(因为在你之前他永远是更优选择)。
📝 算法流程
- 基本操作:push, pop, front, back
- 单调队列:维护单调性 + 窗口范围
- 双端队列 deque:两端都可以进出
$$\text{单调队列:每个元素最多入队出队各一次,总计} O(n)$$
📊 复杂度分析
| 指标 | 复杂度 |
|---|---|
| 时间 | $O(n)$(单调队列) |
| 空间 | $O(n)$ |
💻 参考实现(C++)
C++ (C++17)
#include <bits/stdc++.h>
using namespace std;
// 单调队列:滑动窗口最小值
int main() {
int n, k; cin >> n >> k;
vector<int> a(n);
for (auto& x : a) cin >> x;
deque<int> dq; // 存下标
for (int i = 0; i < n; i++) {
while (!dq.empty() && a[dq.back()] >= a[i]) dq.pop_back();
dq.push_back(i);
if (dq.front() <= i - k) dq.pop_front();
if (i >= k - 1) cout << a[dq.front()] << " ";
}
return 0;
}⚠️ 常见坑点
单调队列弹出条件要注意是 > 还是 >=
窗口过期判断要用下标差
deque 的 pop_front/pop_back 不能在空队列上调用
用数组模拟队列时注意循环队列的取模
📚 相关题目
| 题目 | 来源 | 难度 | 备注 |
|---|---|---|---|
| P1886 滑动窗口 | 洛谷 | CSP-S | 单调队列模板 |
| P2032 扫描 | 洛谷 | CSP-S | 单调队列 |