mobile wallpaper 1mobile wallpaper 2mobile wallpaper 3mobile wallpaper 4mobile wallpaper 5mobile wallpaper 6mobile wallpaper 7mobile wallpaper 8mobile wallpaper 9mobile wallpaper 10mobile wallpaper 11mobile wallpaper 12mobile wallpaper 13
13293 字
34 分鐘
栈 队列 堆 哈希
2026-06-03

第 6 章 栈、队列、堆与哈希专题#

本章目标:深入掌握四类高频结构在算法题中的模型化用法:单调栈、单调队列、优先队列和哈希状态。


6.1 学习目标#

  1. 掌握单调栈求最近更大/更小元素。
  2. 掌握单调队列维护滑动窗口最值。
  3. 掌握堆在动态最值和最短路中的用法。
  4. 掌握哈希表计数、去重和状态压缩。
  5. 能分析每个元素只进出一次带来的 O(n)O(n) 复杂度。

6.2 单调栈#

单调栈维护一个单调序列。它解决的问题通常是:对每个位置,找左边或右边第一个比它大/小的元素。

以找右边第一个更大元素为例:

vector<int> ans(n, -1);
stack<int> st;
for (int i = 0; i < n; i++) {
while (!st.empty() && a[i] > a[st.top()]) {
ans[st.top()] = i;
st.pop();
}
st.push(i);
}

每个下标最多入栈一次、出栈一次,所以总复杂度是 O(n)O(n)


6.3 柱状图最大矩形#

对每个柱子 i,如果知道左边第一个比它矮的位置 L 和右边第一个比它矮的位置 R,则以 i 为最矮柱子的最大矩形面积为:

areai=hi(RiLi1)area_i = h_i \cdot (R_i - L_i - 1)

用单调栈可在线性时间求所有 L 和 R。


6.4 单调队列#

滑动窗口最大值:队列中保存下标,并保证对应值单调递减。

deque<int> q;
for (int i = 0; i < n; i++) {
while (!q.empty() && q.front() <= i - k) q.pop_front();
while (!q.empty() && a[q.back()] <= a[i]) q.pop_back();
q.push_back(i);
if (i >= k - 1) ans.push_back(a[q.front()]);
}

队首永远是当前窗口最大值下标。


6.5 堆的高级用法#

6.5.1 Top K#

维护大小为 k 的小根堆。遍历元素 x:

  1. 堆未满:直接加入。
  2. x 大于堆顶:弹出堆顶,加入 x。
  3. 否则忽略。

最终堆中是前 k 大元素。

6.5.2 双堆维护中位数#

  • 大根堆 small 保存较小一半。
  • 小根堆 large 保存较大一半。
  • 保证 size(small)size(large)1|size(small)-size(large)| \le 1

中位数由堆顶得到。


6.6 哈希计数#

两数之和:遍历 x,检查 target - x 是否出现过。

unordered_map<int, int> cnt;
for (int x : a) {
if (cnt[target - x] > 0) return true;
cnt[x]++;
}

哈希的核心是把“过去的信息”压缩成能快速查询的表。


6.7 状态哈希#

有些搜索状态是数组、字符串或棋盘,可以编码为字符串或整数作为哈希键。

例如 8 数码棋盘:

1 2 3
4 5 6
7 8 0

可编码为字符串 123456780。

状态哈希常用于:

  • BFS 判重。
  • 记忆化搜索。
  • 子串去重。
  • 图状态压缩。

6.8 字符串哈希简述#

多项式哈希:

H(s)=i=0n1sipimodMH(s)=\sum_{i=0}^{n-1} s_i p^i \bmod M

子串哈希可用前缀哈希快速计算:

H(l,r)=H(r)H(l)prlH(l,r)=H(r)-H(l)\cdot p^{r-l}

哈希有碰撞概率,严谨场景可用双哈希。


6.9 易错点#

  1. 单调栈中存值还是存下标混乱。
  2. 单调队列忘记删除过期下标。
  3. 堆中旧元素未删除导致答案错误。
  4. 哈希表使用默认值造成逻辑误判。
  5. 字符串哈希忘记处理负数取模。
  6. 状态编码不唯一。

6.10 练习#

  1. 求每个元素右侧第一个更大元素。
  2. 求柱状图最大矩形。
  3. 维护滑动窗口最大值。
  4. 用双堆维护数据流中位数。
  5. 用哈希表做 BFS 判重。

ACM/OI 扩展:单调结构、可并堆与哈希技巧#

本节补充 OIer 和 ACMer 常用的算法套路、模板、复杂度分析和易错点。 写模板时默认使用 C++17,变量名尽量短,但注释要说明不变量和适用条件。

1. 单调栈四种方向#

单调栈题要明确找左/右、第一个大/小。

目标遍历方向栈维护弹出条件
右侧第一个更大左到右递减当前 > 栈顶
右侧第一个更小左到右递增当前 < 栈顶
左侧第一个更大左到右递减栈顶 <= 当前
左侧第一个更小左到右递增栈顶 >= 当前

要点

  • 相等时保留还是弹出取决于题目要严格还是非严格。

2. 单调队列优化 DP#

当转移中需要窗口最值,可用单调队列。

deque<int> q;
for (int i = 1; i <= n; ++i) {
while (!q.empty() && q.front() < i - k) q.pop_front();
if (!q.empty()) dp[i] = val(q.front(), i);
while (!q.empty() && key(q.back()) >= key(i)) q.pop_back();
q.push_back(i);
}

要点

  • 队列存候选下标,不只存值。
  • 过期规则和单调规则分开写。

3. 懒删除堆#

堆不能删除任意元素,可用两个堆或计数表懒删除。

priority_queue<int> pq, del;
void erase(int x) { del.push(x); }
void normalize() {
while (!pq.empty() && !del.empty() && pq.top() == del.top()) {
pq.pop(); del.pop();
}
}

要点

  • 适合滑动窗口中位数等需要删除旧元素的问题。
  • 也可用 multiset 直接删除。

4. 自定义哈希防卡#

Codeforces 等平台可能卡 unordered_map,可加入 splitmix64。

struct custom_hash {
static uint64_t splitmix64(uint64_t x) {
x += 0x9e3779b97f4a7c15;
x = (x ^ (x >> 30)) * 0xbf58476d1ce4e5b9;
x = (x ^ (x >> 27)) * 0x94d049bb133111eb;
return x ^ (x >> 31);
}
size_t operator()(uint64_t x) const {
static const uint64_t FIXED_RANDOM = chrono::steady_clock::now().time_since_epoch().count();
return splitmix64(x + FIXED_RANDOM);
}
};
unordered_map<long long, int, custom_hash> mp;

要点

  • 不能保证绝对安全,但能降低被构造数据卡掉概率。

5. 栈模拟递归#

递归深度可能达到 2e5 时,C++ 容易爆栈,可改显式栈。

vector<int> order;
stack<pair<int,int>> st;
st.push({root, 0});
while (!st.empty()) {
auto [u, p] = st.top(); st.pop();
order.push_back(u);
for (int v : g[u]) if (v != p) st.push({v, u});
}

要点

  • 树形 DP 若需要后序,可先得到 order 再反向遍历。

本章模板背诵清单#

  • 能在 5 分钟内写出本章第一个核心模板。
  • 能说出每个模板的时间复杂度和空间复杂度。
  • 能说明模板不适用的反例。
  • 能为模板构造 3 个边界样例。
  • 能把模板改成 0-index 或 1-index 版本。

更多竞赛结构模型#

01 Trie 最大异或#

二进制 Trie 用于最大异或、最小异或、带删除异或集合。

struct BinaryTrie {
struct Node { int ch[2] = {0, 0}; int cnt = 0; };
vector<Node> tr{{}};
void insert(int x) {
int u = 0; tr[u].cnt++;
for (int k = 30; k >= 0; --k) {
int b = x >> k & 1;
if (!tr[u].ch[b]) tr[u].ch[b] = tr.size(), tr.push_back({});
u = tr[u].ch[b]; tr[u].cnt++;
}
}
int max_xor(int x) {
int u = 0, ans = 0;
for (int k = 30; k >= 0; --k) {
int b = x >> k & 1;
int go = tr[u].ch[b ^ 1] ? b ^ 1 : b;
if (go != b) ans |= 1 << k;
u = tr[u].ch[go];
}
return ans;
}
};
  • 从高位到低位贪心。
  • 最大异或优先走相反位。
  • 可加 cnt 支持删除。

可并堆概念#

可并堆支持快速合并两个堆,常见左偏树、配对堆。

  • 适合每个节点维护一个堆并在树上合并。
  • 左偏树可用于可删除堆顶、合并集合。
  • 实现比 priority_queue 复杂,只有需要 merge 时才用。

双端队列 0-1 BFS#

边权只有 0/1 时,deque 可以替代堆。

  • 0 边 push_front。
  • 1 边 push_back。
  • 复杂度线性。

哈希状态压缩#

多维状态可以编码成 long long。

long long encode(int x, int y, int mask) {
return ((long long)x << 40) | ((long long)y << 20) | mask;
}
  • 每个字段位数要足够。
  • 负数要偏移成非负。

LRU 思维与链表哈希#

工程面试中 LRU 常用双向链表 + 哈希表。

  • 哈希表定位节点。
  • 链表维护最近使用顺序。
  • get/put 都是 O(1)O(1)

高频模板训练卡#

这一组训练卡用于把“06_栈_队列_堆_哈希”转化为赛场识别能力。每张卡都包含题目信号、模板选择、复杂度、反例和实现检查。

卡 1:单调栈最近更大#

  • 题目信号:需要维护最近关系、动态最值、快速查找或 单调栈最近更大。
  • 优先模板:使用 单调栈最近更大 模板。
  • 核心不变量:每个元素的进入、离开和贡献变化都被准确维护。
  • 复杂度目标:单调结构 O(n),堆 O(n log n),哈希平均 O(n)。
  • 不适用场景:需要有序前驱后继时哈希不够;需要任意删除时普通堆不够。
  • 实现检查:检查重复值、过期元素、哈希键唯一性、空结构访问。
  • 边界样例:全相等、严格递增、严格递减、窗口大小 1。
  • 训练题型:用 单调栈最近更大 写一个模板题和一个变式题。

卡 2:单调栈最近更小#

  • 题目信号:需要维护最近关系、动态最值、快速查找或 单调栈最近更小。
  • 优先模板:使用 单调栈最近更小 模板。
  • 核心不变量:每个元素的进入、离开和贡献变化都被准确维护。
  • 复杂度目标:单调结构 O(n),堆 O(n log n),哈希平均 O(n)。
  • 不适用场景:需要有序前驱后继时哈希不够;需要任意删除时普通堆不够。
  • 实现检查:检查重复值、过期元素、哈希键唯一性、空结构访问。
  • 边界样例:全相等、严格递增、严格递减、窗口大小 1。
  • 训练题型:用 单调栈最近更小 写一个模板题和一个变式题。

卡 3:柱状图最大矩形#

  • 题目信号:需要维护最近关系、动态最值、快速查找或 柱状图最大矩形。
  • 优先模板:使用 柱状图最大矩形 模板。
  • 核心不变量:每个元素的进入、离开和贡献变化都被准确维护。
  • 复杂度目标:单调结构 O(n),堆 O(n log n),哈希平均 O(n)。
  • 不适用场景:需要有序前驱后继时哈希不够;需要任意删除时普通堆不够。
  • 实现检查:检查重复值、过期元素、哈希键唯一性、空结构访问。
  • 边界样例:全相等、严格递增、严格递减、窗口大小 1。
  • 训练题型:用 柱状图最大矩形 写一个模板题和一个变式题。

卡 4:单调队列窗口最值#

  • 题目信号:需要维护最近关系、动态最值、快速查找或 单调队列窗口最值。
  • 优先模板:使用 单调队列窗口最值 模板。
  • 核心不变量:每个元素的进入、离开和贡献变化都被准确维护。
  • 复杂度目标:单调结构 O(n),堆 O(n log n),哈希平均 O(n)。
  • 不适用场景:需要有序前驱后继时哈希不够;需要任意删除时普通堆不够。
  • 实现检查:检查重复值、过期元素、哈希键唯一性、空结构访问。
  • 边界样例:全相等、严格递增、严格递减、窗口大小 1。
  • 训练题型:用 单调队列窗口最值 写一个模板题和一个变式题。

卡 5:单调队列 DP#

  • 题目信号:需要维护最近关系、动态最值、快速查找或 单调队列 DP。
  • 优先模板:使用 单调队列 DP 模板。
  • 核心不变量:每个元素的进入、离开和贡献变化都被准确维护。
  • 复杂度目标:单调结构 O(n),堆 O(n log n),哈希平均 O(n)。
  • 不适用场景:需要有序前驱后继时哈希不够;需要任意删除时普通堆不够。
  • 实现检查:检查重复值、过期元素、哈希键唯一性、空结构访问。
  • 边界样例:全相等、严格递增、严格递减、窗口大小 1。
  • 训练题型:用 单调队列 DP 写一个模板题和一个变式题。

卡 6:双堆中位数#

  • 题目信号:需要维护最近关系、动态最值、快速查找或 双堆中位数。
  • 优先模板:使用 双堆中位数 模板。
  • 核心不变量:每个元素的进入、离开和贡献变化都被准确维护。
  • 复杂度目标:单调结构 O(n),堆 O(n log n),哈希平均 O(n)。
  • 不适用场景:需要有序前驱后继时哈希不够;需要任意删除时普通堆不够。
  • 实现检查:检查重复值、过期元素、哈希键唯一性、空结构访问。
  • 边界样例:全相等、严格递增、严格递减、窗口大小 1。
  • 训练题型:用 双堆中位数 写一个模板题和一个变式题。

卡 7:懒删除堆#

  • 题目信号:需要维护最近关系、动态最值、快速查找或 懒删除堆。
  • 优先模板:使用 懒删除堆 模板。
  • 核心不变量:每个元素的进入、离开和贡献变化都被准确维护。
  • 复杂度目标:单调结构 O(n),堆 O(n log n),哈希平均 O(n)。
  • 不适用场景:需要有序前驱后继时哈希不够;需要任意删除时普通堆不够。
  • 实现检查:检查重复值、过期元素、哈希键唯一性、空结构访问。
  • 边界样例:全相等、严格递增、严格递减、窗口大小 1。
  • 训练题型:用 懒删除堆 写一个模板题和一个变式题。

卡 8:Top K#

  • 题目信号:需要维护最近关系、动态最值、快速查找或 Top K。
  • 优先模板:使用 Top K 模板。
  • 核心不变量:每个元素的进入、离开和贡献变化都被准确维护。
  • 复杂度目标:单调结构 O(n),堆 O(n log n),哈希平均 O(n)。
  • 不适用场景:需要有序前驱后继时哈希不够;需要任意删除时普通堆不够。
  • 实现检查:检查重复值、过期元素、哈希键唯一性、空结构访问。
  • 边界样例:全相等、严格递增、严格递减、窗口大小 1。
  • 训练题型:用 Top K 写一个模板题和一个变式题。

卡 9:合并 K 路#

  • 题目信号:需要维护最近关系、动态最值、快速查找或 合并 K 路。
  • 优先模板:使用 合并 K 路 模板。
  • 核心不变量:每个元素的进入、离开和贡献变化都被准确维护。
  • 复杂度目标:单调结构 O(n),堆 O(n log n),哈希平均 O(n)。
  • 不适用场景:需要有序前驱后继时哈希不够;需要任意删除时普通堆不够。
  • 实现检查:检查重复值、过期元素、哈希键唯一性、空结构访问。
  • 边界样例:全相等、严格递增、严格递减、窗口大小 1。
  • 训练题型:用 合并 K 路 写一个模板题和一个变式题。

卡 10:0-1 BFS 队列#

  • 题目信号:需要维护最近关系、动态最值、快速查找或 0-1 BFS 队列。
  • 优先模板:使用 0-1 BFS 队列 模板。
  • 核心不变量:每个元素的进入、离开和贡献变化都被准确维护。
  • 复杂度目标:单调结构 O(n),堆 O(n log n),哈希平均 O(n)。
  • 不适用场景:需要有序前驱后继时哈希不够;需要任意删除时普通堆不够。
  • 实现检查:检查重复值、过期元素、哈希键唯一性、空结构访问。
  • 边界样例:全相等、严格递增、严格递减、窗口大小 1。
  • 训练题型:用 0-1 BFS 队列 写一个模板题和一个变式题。

卡 11:哈希计数#

  • 题目信号:需要维护最近关系、动态最值、快速查找或 哈希计数。
  • 优先模板:使用 哈希计数 模板。
  • 核心不变量:每个元素的进入、离开和贡献变化都被准确维护。
  • 复杂度目标:单调结构 O(n),堆 O(n log n),哈希平均 O(n)。
  • 不适用场景:需要有序前驱后继时哈希不够;需要任意删除时普通堆不够。
  • 实现检查:检查重复值、过期元素、哈希键唯一性、空结构访问。
  • 边界样例:全相等、严格递增、严格递减、窗口大小 1。
  • 训练题型:用 哈希计数 写一个模板题和一个变式题。

卡 12:哈希判重#

  • 题目信号:需要维护最近关系、动态最值、快速查找或 哈希判重。
  • 优先模板:使用 哈希判重 模板。
  • 核心不变量:每个元素的进入、离开和贡献变化都被准确维护。
  • 复杂度目标:单调结构 O(n),堆 O(n log n),哈希平均 O(n)。
  • 不适用场景:需要有序前驱后继时哈希不够;需要任意删除时普通堆不够。
  • 实现检查:检查重复值、过期元素、哈希键唯一性、空结构访问。
  • 边界样例:全相等、严格递增、严格递减、窗口大小 1。
  • 训练题型:用 哈希判重 写一个模板题和一个变式题。

卡 13:状态压缩哈希#

  • 题目信号:需要维护最近关系、动态最值、快速查找或 状态压缩哈希。
  • 优先模板:使用 状态压缩哈希 模板。
  • 核心不变量:每个元素的进入、离开和贡献变化都被准确维护。
  • 复杂度目标:单调结构 O(n),堆 O(n log n),哈希平均 O(n)。
  • 不适用场景:需要有序前驱后继时哈希不够;需要任意删除时普通堆不够。
  • 实现检查:检查重复值、过期元素、哈希键唯一性、空结构访问。
  • 边界样例:全相等、严格递增、严格递减、窗口大小 1。
  • 训练题型:用 状态压缩哈希 写一个模板题和一个变式题。

卡 14:字符串哈希#

  • 题目信号:需要维护最近关系、动态最值、快速查找或 字符串哈希。
  • 优先模板:使用 字符串哈希 模板。
  • 核心不变量:每个元素的进入、离开和贡献变化都被准确维护。
  • 复杂度目标:单调结构 O(n),堆 O(n log n),哈希平均 O(n)。
  • 不适用场景:需要有序前驱后继时哈希不够;需要任意删除时普通堆不够。
  • 实现检查:检查重复值、过期元素、哈希键唯一性、空结构访问。
  • 边界样例:全相等、严格递增、严格递减、窗口大小 1。
  • 训练题型:用 字符串哈希 写一个模板题和一个变式题。

卡 15:自定义 hash#

  • 题目信号:需要维护最近关系、动态最值、快速查找或 自定义 hash。
  • 优先模板:使用 自定义 hash 模板。
  • 核心不变量:每个元素的进入、离开和贡献变化都被准确维护。
  • 复杂度目标:单调结构 O(n),堆 O(n log n),哈希平均 O(n)。
  • 不适用场景:需要有序前驱后继时哈希不够;需要任意删除时普通堆不够。
  • 实现检查:检查重复值、过期元素、哈希键唯一性、空结构访问。
  • 边界样例:全相等、严格递增、严格递减、窗口大小 1。
  • 训练题型:用 自定义 hash 写一个模板题和一个变式题。

卡 16:二进制 Trie#

  • 题目信号:需要维护最近关系、动态最值、快速查找或 二进制 Trie。
  • 优先模板:使用 二进制 Trie 模板。
  • 核心不变量:每个元素的进入、离开和贡献变化都被准确维护。
  • 复杂度目标:单调结构 O(n),堆 O(n log n),哈希平均 O(n)。
  • 不适用场景:需要有序前驱后继时哈希不够;需要任意删除时普通堆不够。
  • 实现检查:检查重复值、过期元素、哈希键唯一性、空结构访问。
  • 边界样例:全相等、严格递增、严格递减、窗口大小 1。
  • 训练题型:用 二进制 Trie 写一个模板题和一个变式题。

卡 17:可并堆#

  • 题目信号:需要维护最近关系、动态最值、快速查找或 可并堆。
  • 优先模板:使用 可并堆 模板。
  • 核心不变量:每个元素的进入、离开和贡献变化都被准确维护。
  • 复杂度目标:单调结构 O(n),堆 O(n log n),哈希平均 O(n)。
  • 不适用场景:需要有序前驱后继时哈希不够;需要任意删除时普通堆不够。
  • 实现检查:检查重复值、过期元素、哈希键唯一性、空结构访问。
  • 边界样例:全相等、严格递增、严格递减、窗口大小 1。
  • 训练题型:用 可并堆 写一个模板题和一个变式题。

卡 18:表达式求值#

  • 题目信号:需要维护最近关系、动态最值、快速查找或 表达式求值。
  • 优先模板:使用 表达式求值 模板。
  • 核心不变量:每个元素的进入、离开和贡献变化都被准确维护。
  • 复杂度目标:单调结构 O(n),堆 O(n log n),哈希平均 O(n)。
  • 不适用场景:需要有序前驱后继时哈希不够;需要任意删除时普通堆不够。
  • 实现检查:检查重复值、过期元素、哈希键唯一性、空结构访问。
  • 边界样例:全相等、严格递增、严格递减、窗口大小 1。
  • 训练题型:用 表达式求值 写一个模板题和一个变式题。

卡 19:括号匹配#

  • 题目信号:需要维护最近关系、动态最值、快速查找或 括号匹配。
  • 优先模板:使用 括号匹配 模板。
  • 核心不变量:每个元素的进入、离开和贡献变化都被准确维护。
  • 复杂度目标:单调结构 O(n),堆 O(n log n),哈希平均 O(n)。
  • 不适用场景:需要有序前驱后继时哈希不够;需要任意删除时普通堆不够。
  • 实现检查:检查重复值、过期元素、哈希键唯一性、空结构访问。
  • 边界样例:全相等、严格递增、严格递减、窗口大小 1。
  • 训练题型:用 括号匹配 写一个模板题和一个变式题。

卡 20:LRU 链表哈希#

  • 题目信号:需要维护最近关系、动态最值、快速查找或 LRU 链表哈希。
  • 优先模板:使用 LRU 链表哈希 模板。
  • 核心不变量:每个元素的进入、离开和贡献变化都被准确维护。
  • 复杂度目标:单调结构 O(n),堆 O(n log n),哈希平均 O(n)。
  • 不适用场景:需要有序前驱后继时哈希不够;需要任意删除时普通堆不够。
  • 实现检查:检查重复值、过期元素、哈希键唯一性、空结构访问。
  • 边界样例:全相等、严格递增、严格递减、窗口大小 1。
  • 训练题型:用 LRU 链表哈希 写一个模板题和一个变式题。

卡 21:滑动窗口哈希#

  • 题目信号:需要维护最近关系、动态最值、快速查找或 滑动窗口哈希。
  • 优先模板:使用 滑动窗口哈希 模板。
  • 核心不变量:每个元素的进入、离开和贡献变化都被准确维护。
  • 复杂度目标:单调结构 O(n),堆 O(n log n),哈希平均 O(n)。
  • 不适用场景:需要有序前驱后继时哈希不够;需要任意删除时普通堆不够。
  • 实现检查:检查重复值、过期元素、哈希键唯一性、空结构访问。
  • 边界样例:全相等、严格递增、严格递减、窗口大小 1。
  • 训练题型:用 滑动窗口哈希 写一个模板题和一个变式题。

卡 22:频率桶#

  • 题目信号:需要维护最近关系、动态最值、快速查找或 频率桶。
  • 优先模板:使用 频率桶 模板。
  • 核心不变量:每个元素的进入、离开和贡献变化都被准确维护。
  • 复杂度目标:单调结构 O(n),堆 O(n log n),哈希平均 O(n)。
  • 不适用场景:需要有序前驱后继时哈希不够;需要任意删除时普通堆不够。
  • 实现检查:检查重复值、过期元素、哈希键唯一性、空结构访问。
  • 边界样例:全相等、严格递增、严格递减、窗口大小 1。
  • 训练题型:用 频率桶 写一个模板题和一个变式题。

本章赛前默写任务#

  • 默写 单调栈最近更大 的模板,并写出 单调结构 O(n),堆 O(n log n),哈希平均 O(n)。。
  • 默写 单调栈最近更小 的模板,并写出 单调结构 O(n),堆 O(n log n),哈希平均 O(n)。。
  • 默写 柱状图最大矩形 的模板,并写出 单调结构 O(n),堆 O(n log n),哈希平均 O(n)。。
  • 默写 单调队列窗口最值 的模板,并写出 单调结构 O(n),堆 O(n log n),哈希平均 O(n)。。
  • 默写 单调队列 DP 的模板,并写出 单调结构 O(n),堆 O(n log n),哈希平均 O(n)。。
  • 默写 双堆中位数 的模板,并写出 单调结构 O(n),堆 O(n log n),哈希平均 O(n)。。
  • 默写 懒删除堆 的模板,并写出 单调结构 O(n),堆 O(n log n),哈希平均 O(n)。。
  • 默写 Top K 的模板,并写出 单调结构 O(n),堆 O(n log n),哈希平均 O(n)。。
  • 默写 合并 K 路 的模板,并写出 单调结构 O(n),堆 O(n log n),哈希平均 O(n)。。
  • 默写 0-1 BFS 队列 的模板,并写出 单调结构 O(n),堆 O(n log n),哈希平均 O(n)。。
  • 为本章任选 3 个模板各写一个最小输入。
  • 为本章任选 3 个模板各写一个会使错误实现失败的反例。
  • 把本章所有模板整理到个人代码库,并标注 0-index / 1-index。

06_栈_队列_堆_哈希:详细训练清单#

本清单用于把章节知识拆成可执行的小任务,偏 OI/ACM 赛场使用。

训练点 1:单调栈#

  • 识别信号:题面出现与“单调栈”相关的限制、操作或查询时,先判断能否套用本章模型。
  • 建模步骤:写出输入对象、维护状态、允许操作、目标答案,再决定使用暴力、模板或优化。
  • 模板要求:能默写核心代码,并说明变量含义、初始化方式和循环边界。
  • 复杂度要求:写出预处理、单次操作、总复杂度,并确认能通过最大数据范围。
  • 反例检查:构造最小规模、全相等、极端值、重复值、无解或刚好可行的样例。
  • 复盘要求:若做错,记录错因是建模、证明、复杂度、边界、溢出还是模板不熟。
  • 扩展任务:把“单调栈”与至少一个其他专题组合,例如二分、图论、DP、数据结构或数学。

训练点 2:柱状图矩形#

  • 识别信号:题面出现与“柱状图矩形”相关的限制、操作或查询时,先判断能否套用本章模型。
  • 建模步骤:写出输入对象、维护状态、允许操作、目标答案,再决定使用暴力、模板或优化。
  • 模板要求:能默写核心代码,并说明变量含义、初始化方式和循环边界。
  • 复杂度要求:写出预处理、单次操作、总复杂度,并确认能通过最大数据范围。
  • 反例检查:构造最小规模、全相等、极端值、重复值、无解或刚好可行的样例。
  • 复盘要求:若做错,记录错因是建模、证明、复杂度、边界、溢出还是模板不熟。
  • 扩展任务:把“柱状图矩形”与至少一个其他专题组合,例如二分、图论、DP、数据结构或数学。

训练点 3:单调队列#

  • 识别信号:题面出现与“单调队列”相关的限制、操作或查询时,先判断能否套用本章模型。
  • 建模步骤:写出输入对象、维护状态、允许操作、目标答案,再决定使用暴力、模板或优化。
  • 模板要求:能默写核心代码,并说明变量含义、初始化方式和循环边界。
  • 复杂度要求:写出预处理、单次操作、总复杂度,并确认能通过最大数据范围。
  • 反例检查:构造最小规模、全相等、极端值、重复值、无解或刚好可行的样例。
  • 复盘要求:若做错,记录错因是建模、证明、复杂度、边界、溢出还是模板不熟。
  • 扩展任务:把“单调队列”与至少一个其他专题组合,例如二分、图论、DP、数据结构或数学。

训练点 4:滑动窗口最值#

  • 识别信号:题面出现与“滑动窗口最值”相关的限制、操作或查询时,先判断能否套用本章模型。
  • 建模步骤:写出输入对象、维护状态、允许操作、目标答案,再决定使用暴力、模板或优化。
  • 模板要求:能默写核心代码,并说明变量含义、初始化方式和循环边界。
  • 复杂度要求:写出预处理、单次操作、总复杂度,并确认能通过最大数据范围。
  • 反例检查:构造最小规模、全相等、极端值、重复值、无解或刚好可行的样例。
  • 复盘要求:若做错,记录错因是建模、证明、复杂度、边界、溢出还是模板不熟。
  • 扩展任务:把“滑动窗口最值”与至少一个其他专题组合,例如二分、图论、DP、数据结构或数学。

训练点 5:单调队列 DP#

  • 识别信号:题面出现与“单调队列 DP”相关的限制、操作或查询时,先判断能否套用本章模型。
  • 建模步骤:写出输入对象、维护状态、允许操作、目标答案,再决定使用暴力、模板或优化。
  • 模板要求:能默写核心代码,并说明变量含义、初始化方式和循环边界。
  • 复杂度要求:写出预处理、单次操作、总复杂度,并确认能通过最大数据范围。
  • 反例检查:构造最小规模、全相等、极端值、重复值、无解或刚好可行的样例。
  • 复盘要求:若做错,记录错因是建模、证明、复杂度、边界、溢出还是模板不熟。
  • 扩展任务:把“单调队列 DP”与至少一个其他专题组合,例如二分、图论、DP、数据结构或数学。

训练点 6:双堆中位数#

  • 识别信号:题面出现与“双堆中位数”相关的限制、操作或查询时,先判断能否套用本章模型。
  • 建模步骤:写出输入对象、维护状态、允许操作、目标答案,再决定使用暴力、模板或优化。
  • 模板要求:能默写核心代码,并说明变量含义、初始化方式和循环边界。
  • 复杂度要求:写出预处理、单次操作、总复杂度,并确认能通过最大数据范围。
  • 反例检查:构造最小规模、全相等、极端值、重复值、无解或刚好可行的样例。
  • 复盘要求:若做错,记录错因是建模、证明、复杂度、边界、溢出还是模板不熟。
  • 扩展任务:把“双堆中位数”与至少一个其他专题组合,例如二分、图论、DP、数据结构或数学。

训练点 7:懒删除堆#

  • 识别信号:题面出现与“懒删除堆”相关的限制、操作或查询时,先判断能否套用本章模型。
  • 建模步骤:写出输入对象、维护状态、允许操作、目标答案,再决定使用暴力、模板或优化。
  • 模板要求:能默写核心代码,并说明变量含义、初始化方式和循环边界。
  • 复杂度要求:写出预处理、单次操作、总复杂度,并确认能通过最大数据范围。
  • 反例检查:构造最小规模、全相等、极端值、重复值、无解或刚好可行的样例。
  • 复盘要求:若做错,记录错因是建模、证明、复杂度、边界、溢出还是模板不熟。
  • 扩展任务:把“懒删除堆”与至少一个其他专题组合,例如二分、图论、DP、数据结构或数学。

训练点 8:Top K#

  • 识别信号:题面出现与“Top K”相关的限制、操作或查询时,先判断能否套用本章模型。
  • 建模步骤:写出输入对象、维护状态、允许操作、目标答案,再决定使用暴力、模板或优化。
  • 模板要求:能默写核心代码,并说明变量含义、初始化方式和循环边界。
  • 复杂度要求:写出预处理、单次操作、总复杂度,并确认能通过最大数据范围。
  • 反例检查:构造最小规模、全相等、极端值、重复值、无解或刚好可行的样例。
  • 复盘要求:若做错,记录错因是建模、证明、复杂度、边界、溢出还是模板不熟。
  • 扩展任务:把“Top K”与至少一个其他专题组合,例如二分、图论、DP、数据结构或数学。

训练点 9:合并 K 路#

  • 识别信号:题面出现与“合并 K 路”相关的限制、操作或查询时,先判断能否套用本章模型。
  • 建模步骤:写出输入对象、维护状态、允许操作、目标答案,再决定使用暴力、模板或优化。
  • 模板要求:能默写核心代码,并说明变量含义、初始化方式和循环边界。
  • 复杂度要求:写出预处理、单次操作、总复杂度,并确认能通过最大数据范围。
  • 反例检查:构造最小规模、全相等、极端值、重复值、无解或刚好可行的样例。
  • 复盘要求:若做错,记录错因是建模、证明、复杂度、边界、溢出还是模板不熟。
  • 扩展任务:把“合并 K 路”与至少一个其他专题组合,例如二分、图论、DP、数据结构或数学。

训练点 10:哈希计数#

  • 识别信号:题面出现与“哈希计数”相关的限制、操作或查询时,先判断能否套用本章模型。
  • 建模步骤:写出输入对象、维护状态、允许操作、目标答案,再决定使用暴力、模板或优化。
  • 模板要求:能默写核心代码,并说明变量含义、初始化方式和循环边界。
  • 复杂度要求:写出预处理、单次操作、总复杂度,并确认能通过最大数据范围。
  • 反例检查:构造最小规模、全相等、极端值、重复值、无解或刚好可行的样例。
  • 复盘要求:若做错,记录错因是建模、证明、复杂度、边界、溢出还是模板不熟。
  • 扩展任务:把“哈希计数”与至少一个其他专题组合,例如二分、图论、DP、数据结构或数学。

训练点 11:哈希判重#

  • 识别信号:题面出现与“哈希判重”相关的限制、操作或查询时,先判断能否套用本章模型。
  • 建模步骤:写出输入对象、维护状态、允许操作、目标答案,再决定使用暴力、模板或优化。
  • 模板要求:能默写核心代码,并说明变量含义、初始化方式和循环边界。
  • 复杂度要求:写出预处理、单次操作、总复杂度,并确认能通过最大数据范围。
  • 反例检查:构造最小规模、全相等、极端值、重复值、无解或刚好可行的样例。
  • 复盘要求:若做错,记录错因是建模、证明、复杂度、边界、溢出还是模板不熟。
  • 扩展任务:把“哈希判重”与至少一个其他专题组合,例如二分、图论、DP、数据结构或数学。

训练点 12:状态编码#

  • 识别信号:题面出现与“状态编码”相关的限制、操作或查询时,先判断能否套用本章模型。
  • 建模步骤:写出输入对象、维护状态、允许操作、目标答案,再决定使用暴力、模板或优化。
  • 模板要求:能默写核心代码,并说明变量含义、初始化方式和循环边界。
  • 复杂度要求:写出预处理、单次操作、总复杂度,并确认能通过最大数据范围。
  • 反例检查:构造最小规模、全相等、极端值、重复值、无解或刚好可行的样例。
  • 复盘要求:若做错,记录错因是建模、证明、复杂度、边界、溢出还是模板不熟。
  • 扩展任务:把“状态编码”与至少一个其他专题组合,例如二分、图论、DP、数据结构或数学。

训练点 13:字符串哈希#

  • 识别信号:题面出现与“字符串哈希”相关的限制、操作或查询时,先判断能否套用本章模型。
  • 建模步骤:写出输入对象、维护状态、允许操作、目标答案,再决定使用暴力、模板或优化。
  • 模板要求:能默写核心代码,并说明变量含义、初始化方式和循环边界。
  • 复杂度要求:写出预处理、单次操作、总复杂度,并确认能通过最大数据范围。
  • 反例检查:构造最小规模、全相等、极端值、重复值、无解或刚好可行的样例。
  • 复盘要求:若做错,记录错因是建模、证明、复杂度、边界、溢出还是模板不熟。
  • 扩展任务:把“字符串哈希”与至少一个其他专题组合,例如二分、图论、DP、数据结构或数学。

训练点 14:二进制 Trie#

  • 识别信号:题面出现与“二进制 Trie”相关的限制、操作或查询时,先判断能否套用本章模型。
  • 建模步骤:写出输入对象、维护状态、允许操作、目标答案,再决定使用暴力、模板或优化。
  • 模板要求:能默写核心代码,并说明变量含义、初始化方式和循环边界。
  • 复杂度要求:写出预处理、单次操作、总复杂度,并确认能通过最大数据范围。
  • 反例检查:构造最小规模、全相等、极端值、重复值、无解或刚好可行的样例。
  • 复盘要求:若做错,记录错因是建模、证明、复杂度、边界、溢出还是模板不熟。
  • 扩展任务:把“二进制 Trie”与至少一个其他专题组合,例如二分、图论、DP、数据结构或数学。

训练点 15:可并堆#

  • 识别信号:题面出现与“可并堆”相关的限制、操作或查询时,先判断能否套用本章模型。
  • 建模步骤:写出输入对象、维护状态、允许操作、目标答案,再决定使用暴力、模板或优化。
  • 模板要求:能默写核心代码,并说明变量含义、初始化方式和循环边界。
  • 复杂度要求:写出预处理、单次操作、总复杂度,并确认能通过最大数据范围。
  • 反例检查:构造最小规模、全相等、极端值、重复值、无解或刚好可行的样例。
  • 复盘要求:若做错,记录错因是建模、证明、复杂度、边界、溢出还是模板不熟。
  • 扩展任务:把“可并堆”与至少一个其他专题组合,例如二分、图论、DP、数据结构或数学。

训练点 16:表达式求值#

  • 识别信号:题面出现与“表达式求值”相关的限制、操作或查询时,先判断能否套用本章模型。
  • 建模步骤:写出输入对象、维护状态、允许操作、目标答案,再决定使用暴力、模板或优化。
  • 模板要求:能默写核心代码,并说明变量含义、初始化方式和循环边界。
  • 复杂度要求:写出预处理、单次操作、总复杂度,并确认能通过最大数据范围。
  • 反例检查:构造最小规模、全相等、极端值、重复值、无解或刚好可行的样例。
  • 复盘要求:若做错,记录错因是建模、证明、复杂度、边界、溢出还是模板不熟。
  • 扩展任务:把“表达式求值”与至少一个其他专题组合,例如二分、图论、DP、数据结构或数学。

训练点 17:单调栈#

  • 识别信号:题面出现与“单调栈”相关的限制、操作或查询时,先判断能否套用本章模型。
  • 建模步骤:写出输入对象、维护状态、允许操作、目标答案,再决定使用暴力、模板或优化。
  • 模板要求:能默写核心代码,并说明变量含义、初始化方式和循环边界。
  • 复杂度要求:写出预处理、单次操作、总复杂度,并确认能通过最大数据范围。
  • 反例检查:构造最小规模、全相等、极端值、重复值、无解或刚好可行的样例。
  • 复盘要求:若做错,记录错因是建模、证明、复杂度、边界、溢出还是模板不熟。
  • 扩展任务:把“单调栈”与至少一个其他专题组合,例如二分、图论、DP、数据结构或数学。

训练点 18:柱状图矩形#

  • 识别信号:题面出现与“柱状图矩形”相关的限制、操作或查询时,先判断能否套用本章模型。
  • 建模步骤:写出输入对象、维护状态、允许操作、目标答案,再决定使用暴力、模板或优化。
  • 模板要求:能默写核心代码,并说明变量含义、初始化方式和循环边界。
  • 复杂度要求:写出预处理、单次操作、总复杂度,并确认能通过最大数据范围。
  • 反例检查:构造最小规模、全相等、极端值、重复值、无解或刚好可行的样例。
  • 复盘要求:若做错,记录错因是建模、证明、复杂度、边界、溢出还是模板不熟。
  • 扩展任务:把“柱状图矩形”与至少一个其他专题组合,例如二分、图论、DP、数据结构或数学。

训练点 19:单调队列#

  • 识别信号:题面出现与“单调队列”相关的限制、操作或查询时,先判断能否套用本章模型。
  • 建模步骤:写出输入对象、维护状态、允许操作、目标答案,再决定使用暴力、模板或优化。
  • 模板要求:能默写核心代码,并说明变量含义、初始化方式和循环边界。
  • 复杂度要求:写出预处理、单次操作、总复杂度,并确认能通过最大数据范围。
  • 反例检查:构造最小规模、全相等、极端值、重复值、无解或刚好可行的样例。
  • 复盘要求:若做错,记录错因是建模、证明、复杂度、边界、溢出还是模板不熟。
  • 扩展任务:把“单调队列”与至少一个其他专题组合,例如二分、图论、DP、数据结构或数学。

训练点 20:滑动窗口最值#

  • 识别信号:题面出现与“滑动窗口最值”相关的限制、操作或查询时,先判断能否套用本章模型。
  • 建模步骤:写出输入对象、维护状态、允许操作、目标答案,再决定使用暴力、模板或优化。
  • 模板要求:能默写核心代码,并说明变量含义、初始化方式和循环边界。
  • 复杂度要求:写出预处理、单次操作、总复杂度,并确认能通过最大数据范围。
  • 反例检查:构造最小规模、全相等、极端值、重复值、无解或刚好可行的样例。
  • 复盘要求:若做错,记录错因是建模、证明、复杂度、边界、溢出还是模板不熟。
  • 扩展任务:把“滑动窗口最值”与至少一个其他专题组合,例如二分、图论、DP、数据结构或数学。

训练点 21:单调队列 DP#

  • 识别信号:题面出现与“单调队列 DP”相关的限制、操作或查询时,先判断能否套用本章模型。
  • 建模步骤:写出输入对象、维护状态、允许操作、目标答案,再决定使用暴力、模板或优化。
  • 模板要求:能默写核心代码,并说明变量含义、初始化方式和循环边界。
  • 复杂度要求:写出预处理、单次操作、总复杂度,并确认能通过最大数据范围。
  • 反例检查:构造最小规模、全相等、极端值、重复值、无解或刚好可行的样例。
  • 复盘要求:若做错,记录错因是建模、证明、复杂度、边界、溢出还是模板不熟。
  • 扩展任务:把“单调队列 DP”与至少一个其他专题组合,例如二分、图论、DP、数据结构或数学。

训练点 22:双堆中位数#

  • 识别信号:题面出现与“双堆中位数”相关的限制、操作或查询时,先判断能否套用本章模型。
  • 建模步骤:写出输入对象、维护状态、允许操作、目标答案,再决定使用暴力、模板或优化。
  • 模板要求:能默写核心代码,并说明变量含义、初始化方式和循环边界。
  • 复杂度要求:写出预处理、单次操作、总复杂度,并确认能通过最大数据范围。
  • 反例检查:构造最小规模、全相等、极端值、重复值、无解或刚好可行的样例。
  • 复盘要求:若做错,记录错因是建模、证明、复杂度、边界、溢出还是模板不熟。
  • 扩展任务:把“双堆中位数”与至少一个其他专题组合,例如二分、图论、DP、数据结构或数学。

训练点 23:懒删除堆#

  • 识别信号:题面出现与“懒删除堆”相关的限制、操作或查询时,先判断能否套用本章模型。
  • 建模步骤:写出输入对象、维护状态、允许操作、目标答案,再决定使用暴力、模板或优化。
  • 模板要求:能默写核心代码,并说明变量含义、初始化方式和循环边界。
  • 复杂度要求:写出预处理、单次操作、总复杂度,并确认能通过最大数据范围。
  • 反例检查:构造最小规模、全相等、极端值、重复值、无解或刚好可行的样例。
  • 复盘要求:若做错,记录错因是建模、证明、复杂度、边界、溢出还是模板不熟。
  • 扩展任务:把“懒删除堆”与至少一个其他专题组合,例如二分、图论、DP、数据结构或数学。

训练点 24:Top K#

  • 识别信号:题面出现与“Top K”相关的限制、操作或查询时,先判断能否套用本章模型。
  • 建模步骤:写出输入对象、维护状态、允许操作、目标答案,再决定使用暴力、模板或优化。
  • 模板要求:能默写核心代码,并说明变量含义、初始化方式和循环边界。
  • 复杂度要求:写出预处理、单次操作、总复杂度,并确认能通过最大数据范围。
  • 反例检查:构造最小规模、全相等、极端值、重复值、无解或刚好可行的样例。
  • 复盘要求:若做错,记录错因是建模、证明、复杂度、边界、溢出还是模板不熟。
  • 扩展任务:把“Top K”与至少一个其他专题组合,例如二分、图论、DP、数据结构或数学。
分享

如果這篇文章對你有幫助,歡迎分享給更多人!

栈 队列 堆 哈希
https://lemusakuya.com/posts/study-notes/algorithm/06_栈_队列_堆_哈希/
作者
レム・咲く夜
發布於
2026-06-03
許可協議
CC BY-NC-SA 4.0

部分資訊可能已經過時

目錄