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
12059 字
31 分鐘
基础数据结构与语言工具
2026-06-03

第 2 章 基础数据结构与语言工具#

本章目标:理解常用数据结构的能力边界,知道什么时候该选数组、栈、队列、堆、哈希表或树。


2.1 学习目标#

  1. 掌握数组、链表、栈、队列、堆、哈希表的基本操作和复杂度。
  2. 能根据题目操作类型选择合适数据结构。
  3. 熟悉 C++ STL 或 Python 常用容器的复杂度。
  4. 理解“接口”和“实现”的区别。
  5. 避免因容器误用导致超时或错误。

2.2 数组与动态数组#

数组是一段连续内存。优点是随机访问快:

access(ai)=O(1)\text{access}(a_i)=O(1)

缺点是中间插入和删除需要移动元素:

insert/delete in middle=O(n)\text{insert/delete in middle}=O(n)

动态数组如 C++ 的 vector、Python 的 list,会在容量不足时扩容。单次 append 可能是 O(n)O(n),但均摊是 O(1)O(1)

均摊直觉:扩容不是每次发生,总移动成本可以分摊到多次插入上。


2.3 链表#

链表节点保存值和指针:

[value | next] -> [value | next] -> ...

链表适合已经知道节点位置后的插入删除:O(1)O(1)。但查找第 k 个元素需要从头走:O(n)O(n)

面试中的链表题常考:

  • 快慢指针找环。
  • 反转链表。
  • 合并有序链表。
  • 删除倒数第 k 个节点。

2.4 栈#

栈是后进先出结构:

push -> 栈顶
pop <- 栈顶

典型用途:

  1. 括号匹配。
  2. 表达式求值。
  3. 单调栈。
  4. DFS 的显式栈。
  5. 函数调用栈。

括号匹配伪代码:

for (char c : s) {
if (c == '(') st.push(c);
else {
if (st.empty()) return false;
st.pop();
}
}
return st.empty();

2.5 队列与双端队列#

队列是先进先出,适合 BFS:

push back -> [old ... new] -> pop front

双端队列 deque 两端都能进出,常用于单调队列维护窗口最值。

滑动窗口最大值中,每个元素最多入队一次、出队一次,因此总复杂度是 O(n)O(n)


2.6 堆与优先队列#

堆支持快速取最小或最大元素。

操作复杂度
取堆顶O(1)O(1)
插入O(logn)O(\log n)
删除堆顶O(logn)O(\log n)

典型用途:

  • Top K。
  • 合并 k 个有序序列。
  • Dijkstra 最短路。
  • Huffman 编码。
  • 动态中位数。

Dijkstra 中堆内可能存在过期状态,常用懒删除:

while (!pq.empty()) {
auto [d, u] = pq.top(); pq.pop();
if (d != dist[u]) continue;
relax(u);
}

2.7 哈希表#

哈希表把键映射到桶,平均查找复杂度接近 O(1)O(1)

index=hash(key)modmindex = hash(key) \bmod m

注意:

  1. 键必须能唯一表示状态。
  2. 哈希冲突不可避免。
  3. 自定义结构要提供哈希函数。
  4. 字符串哈希可能碰撞,重要场景要二次校验。

2.8 常用容器复杂度#

容器查找插入删除备注
vector/list按下标 O(1)O(1)尾部均摊 O(1)O(1)中间 O(n)O(n)连续内存
stack栈顶 O(1)O(1)O(1)O(1)O(1)O(1)LIFO
queue队首 O(1)O(1)O(1)O(1)O(1)O(1)FIFO
priority_queue堆顶 O(1)O(1)O(logn)O(\log n)O(logn)O(\log n)动态最值
unordered_map平均 O(1)O(1)平均 O(1)O(1)平均 O(1)O(1)哈希
map/setO(logn)O(\log n)O(logn)O(\log n)O(logn)O(\log n)有序树

2.9 选择数据结构的思路#

先问题目需要什么操作:

需求常用结构
随机访问数组、vector
最近未匹配元素
层序扩展队列
动态最大/最小
快速存在性判断哈希表
有序集合、前驱后继平衡树、set
区间查询修改树状数组、线段树

不要从“我会什么结构”出发,而要从“题目需要什么操作”出发。


2.10 易错点#

  1. 在 vector 中间频繁 erase 导致 O(n2)O(n^2)
  2. 用 set 但其实不需要有序,常数较大。
  3. priority_queue 默认是大根堆。
  4. unordered_map 的键设计不唯一。
  5. Python list 头部 pop(0) 是 O(n)O(n),队列应使用 deque。
  6. 使用递归 DFS 时忘记栈深度限制。

2.11 练习#

  1. 说明为什么动态数组 append 的均摊复杂度是 O(1)O(1)
  2. 用栈判断括号序列是否合法。
  3. 用堆维护数据流中的前 k 大元素。
  4. 用哈希表统计数组中两数之和的方案。
  5. 写出 Python deque 维护滑动窗口最大值的伪代码。

ACM/OI 扩展:竞赛常用容器与手写结构#

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

1. vector 邻接表#

大多数图论题用 vector 邻接表足够。若边数极大且需要极致性能,可用链式前向星。

vector<vector<pair<int,int>>> g(n + 1);
for (int i = 0; i < m; ++i) {
int u, v, w;
cin >> u >> v >> w;
g[u].push_back({v, w});
// g[v].push_back({u, w}); // 无向图打开
}

要点

  • 写无向图时加双向边。
  • 需要清空时 g.assign(n + 1, {})
  • 若递归 DFS 爆栈,改成迭代或提高栈限制。

2. 链式前向星#

OI 传统写法,适合边数上限固定、需要频繁清空或性能敏感的图。

const int N = 200000 + 5;
const int M = 400000 + 5;
int head[N], to[M], nxt[M], w[M], ecnt;
void init(int n) {
fill(head, head + n + 1, -1);
ecnt = 0;
}
void addEdge(int u, int v, int c) {
to[ecnt] = v; w[ecnt] = c; nxt[ecnt] = head[u]; head[u] = ecnt++;
}
for (int e = head[u]; e != -1; e = nxt[e]) {
int v = to[e], c = w[e];
}

要点

  • 无向图需要调用两次 addEdge。
  • 数组 M 要按边数乘 2。
  • 遍历时 e 是边编号,不是点编号。

常见坑

  • 忘记 init head 为 -1。
  • M 开小导致越界。

3. 手写队列#

BFS 中 std::queue 通常够用;极限性能可用数组队列。

int q[N], hh = 0, tt = 0;
q[tt++] = s;
while (hh < tt) {
int u = q[hh++];
for (int v : g[u]) {
if (!vis[v]) {
vis[v] = 1;
q[tt++] = v;
}
}
}

要点

  • 数组队列不支持自动扩容。
  • BFS 入队时标记,避免重复入队。

4. policy based data structure#

GCC 扩展 tree 可维护有序集合并查询排名,部分 OJ 支持,部分不支持。

#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
using namespace __gnu_pbds;
template<class T>
using ordered_set = tree<T, null_type, less<T>, rb_tree_tag, tree_order_statistics_node_update>;
ordered_set<pair<int,int>> os;
// os.order_of_key(x): 小于 x 的数量
// *os.find_by_order(k): 第 k 小,0-index

要点

  • 处理重复元素时存 pair(value, id)。
  • 不是标准 C++,比赛前确认评测环境。

5. 坐标压缩模板#

值域很大但只关心相对大小时使用离散化。

vector<int> xs = a;
sort(xs.begin(), xs.end());
xs.erase(unique(xs.begin(), xs.end()), xs.end());
a[i] = lower_bound(xs.begin(), xs.end(), a[i]) - xs.begin() + 1;

要点

  • 树状数组通常压缩到 1-index。
  • 若需要原始距离,不能只保留排名。

训练题型

  • 用离散化 + 树状数组求逆序对。
  • 用离散化处理区间覆盖长度。

本章模板背诵清单#

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

STL 与手写结构补充#

multiset 的删除#

erase(value) 会删除所有等于 value 的元素;只删一个要用迭代器。

auto it = ms.find(x);
if (it != ms.end()) ms.erase(it);
  • 滑动窗口中位数常用 multiset。
  • 有重复值时必须小心 erase。

map / unordered_map 选择#

如果需要顺序、前驱后继,使用 map/set;只需要计数和存在性,使用 unordered_map。

  • map 是红黑树,O(logn)O(\log n)
  • unordered_map 平均 O(1)O(1),但可能被卡。
  • 键是 pair 时需要自定义 hash。

bitset 优化#

当布尔状态很多,bitset 可以用位运算一次处理 64 位或更多。

bitset<MAXN> reach;
reach[0] = 1;
for (int w : weights) reach |= (reach << w);
  • 01 背包可用 bitset 优化可达性。
  • 传递闭包可用 bitset 优化 Floyd。

pair / tuple 排序#

C++ pair 默认先按 first,再按 second 字典序排序。

vector<pair<int,int>> a;
sort(a.begin(), a.end());
  • 区间按左端点排序可直接用 pair。
  • 需要降序时写自定义比较器。

数组初始化技巧#

全局数组默认初始化为 0,局部大数组可能爆栈。

memset(dist, 0x3f, sizeof dist); // int INF
fill(vec.begin(), vec.end(), LINF);
  • memset 只适合 0、-1、0x3f 这类字节模式。
  • long long 数组用 fill 更稳。

高频模板训练卡#

这一组训练卡用于把“02_基础数据结构与语言工具”转化为赛场识别能力。每张卡都包含题目信号、模板选择、复杂度、反例和实现检查。

卡 1:vector 邻接表#

  • 题目信号:需要 vector 邻接表 支持高频操作。
  • 优先模板:使用 vector 邻接表 的标准 C++17 写法或手写结构。
  • 核心不变量:每次操作后结构内部顺序和计数保持合法。
  • 复杂度目标:通常为 O(1)、O(log n) 或 O(n/word)。
  • 不适用场景:操作需要顺序但使用哈希,或需要删除任意元素但使用堆。
  • 实现检查:检查容器清空、重复元素、迭代器失效、下标起点。
  • 边界样例:空容器、单元素、重复值、删除不存在元素。
  • 训练题型:用 vector 邻接表 写一个最小 demo 并压测 1e5 次操作。

卡 2:链式前向星#

  • 题目信号:需要 链式前向星 支持高频操作。
  • 优先模板:使用 链式前向星 的标准 C++17 写法或手写结构。
  • 核心不变量:每次操作后结构内部顺序和计数保持合法。
  • 复杂度目标:通常为 O(1)、O(log n) 或 O(n/word)。
  • 不适用场景:操作需要顺序但使用哈希,或需要删除任意元素但使用堆。
  • 实现检查:检查容器清空、重复元素、迭代器失效、下标起点。
  • 边界样例:空容器、单元素、重复值、删除不存在元素。
  • 训练题型:用 链式前向星 写一个最小 demo 并压测 1e5 次操作。

卡 3:栈模拟递归#

  • 题目信号:需要 栈模拟递归 支持高频操作。
  • 优先模板:使用 栈模拟递归 的标准 C++17 写法或手写结构。
  • 核心不变量:每次操作后结构内部顺序和计数保持合法。
  • 复杂度目标:通常为 O(1)、O(log n) 或 O(n/word)。
  • 不适用场景:操作需要顺序但使用哈希,或需要删除任意元素但使用堆。
  • 实现检查:检查容器清空、重复元素、迭代器失效、下标起点。
  • 边界样例:空容器、单元素、重复值、删除不存在元素。
  • 训练题型:用 栈模拟递归 写一个最小 demo 并压测 1e5 次操作。

卡 4:数组队列 BFS#

  • 题目信号:需要 数组队列 BFS 支持高频操作。
  • 优先模板:使用 数组队列 BFS 的标准 C++17 写法或手写结构。
  • 核心不变量:每次操作后结构内部顺序和计数保持合法。
  • 复杂度目标:通常为 O(1)、O(log n) 或 O(n/word)。
  • 不适用场景:操作需要顺序但使用哈希,或需要删除任意元素但使用堆。
  • 实现检查:检查容器清空、重复元素、迭代器失效、下标起点。
  • 边界样例:空容器、单元素、重复值、删除不存在元素。
  • 训练题型:用 数组队列 BFS 写一个最小 demo 并压测 1e5 次操作。

卡 5:priority_queue#

  • 题目信号:需要 priority_queue 支持高频操作。
  • 优先模板:使用 priority_queue 的标准 C++17 写法或手写结构。
  • 核心不变量:每次操作后结构内部顺序和计数保持合法。
  • 复杂度目标:通常为 O(1)、O(log n) 或 O(n/word)。
  • 不适用场景:操作需要顺序但使用哈希,或需要删除任意元素但使用堆。
  • 实现检查:检查容器清空、重复元素、迭代器失效、下标起点。
  • 边界样例:空容器、单元素、重复值、删除不存在元素。
  • 训练题型:用 priority_queue 写一个最小 demo 并压测 1e5 次操作。

卡 6:set/multiset#

  • 题目信号:需要 set/multiset 支持高频操作。
  • 优先模板:使用 set/multiset 的标准 C++17 写法或手写结构。
  • 核心不变量:每次操作后结构内部顺序和计数保持合法。
  • 复杂度目标:通常为 O(1)、O(log n) 或 O(n/word)。
  • 不适用场景:操作需要顺序但使用哈希,或需要删除任意元素但使用堆。
  • 实现检查:检查容器清空、重复元素、迭代器失效、下标起点。
  • 边界样例:空容器、单元素、重复值、删除不存在元素。
  • 训练题型:用 set/multiset 写一个最小 demo 并压测 1e5 次操作。

卡 7:unordered_map 防卡#

  • 题目信号:需要 unordered_map 防卡 支持高频操作。
  • 优先模板:使用 unordered_map 防卡 的标准 C++17 写法或手写结构。
  • 核心不变量:每次操作后结构内部顺序和计数保持合法。
  • 复杂度目标:通常为 O(1)、O(log n) 或 O(n/word)。
  • 不适用场景:操作需要顺序但使用哈希,或需要删除任意元素但使用堆。
  • 实现检查:检查容器清空、重复元素、迭代器失效、下标起点。
  • 边界样例:空容器、单元素、重复值、删除不存在元素。
  • 训练题型:用 unordered_map 防卡 写一个最小 demo 并压测 1e5 次操作。

卡 8:bitset 优化#

  • 题目信号:需要 bitset 优化 支持高频操作。
  • 优先模板:使用 bitset 优化 的标准 C++17 写法或手写结构。
  • 核心不变量:每次操作后结构内部顺序和计数保持合法。
  • 复杂度目标:通常为 O(1)、O(log n) 或 O(n/word)。
  • 不适用场景:操作需要顺序但使用哈希,或需要删除任意元素但使用堆。
  • 实现检查:检查容器清空、重复元素、迭代器失效、下标起点。
  • 边界样例:空容器、单元素、重复值、删除不存在元素。
  • 训练题型:用 bitset 优化 写一个最小 demo 并压测 1e5 次操作。

卡 9:坐标压缩#

  • 题目信号:需要 坐标压缩 支持高频操作。
  • 优先模板:使用 坐标压缩 的标准 C++17 写法或手写结构。
  • 核心不变量:每次操作后结构内部顺序和计数保持合法。
  • 复杂度目标:通常为 O(1)、O(log n) 或 O(n/word)。
  • 不适用场景:操作需要顺序但使用哈希,或需要删除任意元素但使用堆。
  • 实现检查:检查容器清空、重复元素、迭代器失效、下标起点。
  • 边界样例:空容器、单元素、重复值、删除不存在元素。
  • 训练题型:用 坐标压缩 写一个最小 demo 并压测 1e5 次操作。

卡 10:pair 排序#

  • 题目信号:需要 pair 排序 支持高频操作。
  • 优先模板:使用 pair 排序 的标准 C++17 写法或手写结构。
  • 核心不变量:每次操作后结构内部顺序和计数保持合法。
  • 复杂度目标:通常为 O(1)、O(log n) 或 O(n/word)。
  • 不适用场景:操作需要顺序但使用哈希,或需要删除任意元素但使用堆。
  • 实现检查:检查容器清空、重复元素、迭代器失效、下标起点。
  • 边界样例:空容器、单元素、重复值、删除不存在元素。
  • 训练题型:用 pair 排序 写一个最小 demo 并压测 1e5 次操作。

卡 11:tuple 状态#

  • 题目信号:需要 tuple 状态 支持高频操作。
  • 优先模板:使用 tuple 状态 的标准 C++17 写法或手写结构。
  • 核心不变量:每次操作后结构内部顺序和计数保持合法。
  • 复杂度目标:通常为 O(1)、O(log n) 或 O(n/word)。
  • 不适用场景:操作需要顺序但使用哈希,或需要删除任意元素但使用堆。
  • 实现检查:检查容器清空、重复元素、迭代器失效、下标起点。
  • 边界样例:空容器、单元素、重复值、删除不存在元素。
  • 训练题型:用 tuple 状态 写一个最小 demo 并压测 1e5 次操作。

卡 12:手写小根堆#

  • 题目信号:需要 手写小根堆 支持高频操作。
  • 优先模板:使用 手写小根堆 的标准 C++17 写法或手写结构。
  • 核心不变量:每次操作后结构内部顺序和计数保持合法。
  • 复杂度目标:通常为 O(1)、O(log n) 或 O(n/word)。
  • 不适用场景:操作需要顺序但使用哈希,或需要删除任意元素但使用堆。
  • 实现检查:检查容器清空、重复元素、迭代器失效、下标起点。
  • 边界样例:空容器、单元素、重复值、删除不存在元素。
  • 训练题型:用 手写小根堆 写一个最小 demo 并压测 1e5 次操作。

卡 13:双端队列#

  • 题目信号:需要 双端队列 支持高频操作。
  • 优先模板:使用 双端队列 的标准 C++17 写法或手写结构。
  • 核心不变量:每次操作后结构内部顺序和计数保持合法。
  • 复杂度目标:通常为 O(1)、O(log n) 或 O(n/word)。
  • 不适用场景:操作需要顺序但使用哈希,或需要删除任意元素但使用堆。
  • 实现检查:检查容器清空、重复元素、迭代器失效、下标起点。
  • 边界样例:空容器、单元素、重复值、删除不存在元素。
  • 训练题型:用 双端队列 写一个最小 demo 并压测 1e5 次操作。

卡 14:环形队列#

  • 题目信号:需要 环形队列 支持高频操作。
  • 优先模板:使用 环形队列 的标准 C++17 写法或手写结构。
  • 核心不变量:每次操作后结构内部顺序和计数保持合法。
  • 复杂度目标:通常为 O(1)、O(log n) 或 O(n/word)。
  • 不适用场景:操作需要顺序但使用哈希,或需要删除任意元素但使用堆。
  • 实现检查:检查容器清空、重复元素、迭代器失效、下标起点。
  • 边界样例:空容器、单元素、重复值、删除不存在元素。
  • 训练题型:用 环形队列 写一个最小 demo 并压测 1e5 次操作。

卡 15:内存池#

  • 题目信号:需要 内存池 支持高频操作。
  • 优先模板:使用 内存池 的标准 C++17 写法或手写结构。
  • 核心不变量:每次操作后结构内部顺序和计数保持合法。
  • 复杂度目标:通常为 O(1)、O(log n) 或 O(n/word)。
  • 不适用场景:操作需要顺序但使用哈希,或需要删除任意元素但使用堆。
  • 实现检查:检查容器清空、重复元素、迭代器失效、下标起点。
  • 边界样例:空容器、单元素、重复值、删除不存在元素。
  • 训练题型:用 内存池 写一个最小 demo 并压测 1e5 次操作。

卡 16:结构体排序#

  • 题目信号:需要 结构体排序 支持高频操作。
  • 优先模板:使用 结构体排序 的标准 C++17 写法或手写结构。
  • 核心不变量:每次操作后结构内部顺序和计数保持合法。
  • 复杂度目标:通常为 O(1)、O(log n) 或 O(n/word)。
  • 不适用场景:操作需要顺序但使用哈希,或需要删除任意元素但使用堆。
  • 实现检查:检查容器清空、重复元素、迭代器失效、下标起点。
  • 边界样例:空容器、单元素、重复值、删除不存在元素。
  • 训练题型:用 结构体排序 写一个最小 demo 并压测 1e5 次操作。

卡 17:全局数组初始化#

  • 题目信号:需要 全局数组初始化 支持高频操作。
  • 优先模板:使用 全局数组初始化 的标准 C++17 写法或手写结构。
  • 核心不变量:每次操作后结构内部顺序和计数保持合法。
  • 复杂度目标:通常为 O(1)、O(log n) 或 O(n/word)。
  • 不适用场景:操作需要顺序但使用哈希,或需要删除任意元素但使用堆。
  • 实现检查:检查容器清空、重复元素、迭代器失效、下标起点。
  • 边界样例:空容器、单元素、重复值、删除不存在元素。
  • 训练题型:用 全局数组初始化 写一个最小 demo 并压测 1e5 次操作。

卡 18:PBDS 有序集合#

  • 题目信号:需要 PBDS 有序集合 支持高频操作。
  • 优先模板:使用 PBDS 有序集合 的标准 C++17 写法或手写结构。
  • 核心不变量:每次操作后结构内部顺序和计数保持合法。
  • 复杂度目标:通常为 O(1)、O(log n) 或 O(n/word)。
  • 不适用场景:操作需要顺序但使用哈希,或需要删除任意元素但使用堆。
  • 实现检查:检查容器清空、重复元素、迭代器失效、下标起点。
  • 边界样例:空容器、单元素、重复值、删除不存在元素。
  • 训练题型:用 PBDS 有序集合 写一个最小 demo 并压测 1e5 次操作。

卡 19:自定义 hash#

  • 题目信号:需要 自定义 hash 支持高频操作。
  • 优先模板:使用 自定义 hash 的标准 C++17 写法或手写结构。
  • 核心不变量:每次操作后结构内部顺序和计数保持合法。
  • 复杂度目标:通常为 O(1)、O(log n) 或 O(n/word)。
  • 不适用场景:操作需要顺序但使用哈希,或需要删除任意元素但使用堆。
  • 实现检查:检查容器清空、重复元素、迭代器失效、下标起点。
  • 边界样例:空容器、单元素、重复值、删除不存在元素。
  • 训练题型:用 自定义 hash 写一个最小 demo 并压测 1e5 次操作。

卡 20:离散化映射#

  • 题目信号:需要 离散化映射 支持高频操作。
  • 优先模板:使用 离散化映射 的标准 C++17 写法或手写结构。
  • 核心不变量:每次操作后结构内部顺序和计数保持合法。
  • 复杂度目标:通常为 O(1)、O(log n) 或 O(n/word)。
  • 不适用场景:操作需要顺序但使用哈希,或需要删除任意元素但使用堆。
  • 实现检查:检查容器清空、重复元素、迭代器失效、下标起点。
  • 边界样例:空容器、单元素、重复值、删除不存在元素。
  • 训练题型:用 离散化映射 写一个最小 demo 并压测 1e5 次操作。

卡 21:桶计数#

  • 题目信号:需要 桶计数 支持高频操作。
  • 优先模板:使用 桶计数 的标准 C++17 写法或手写结构。
  • 核心不变量:每次操作后结构内部顺序和计数保持合法。
  • 复杂度目标:通常为 O(1)、O(log n) 或 O(n/word)。
  • 不适用场景:操作需要顺序但使用哈希,或需要删除任意元素但使用堆。
  • 实现检查:检查容器清空、重复元素、迭代器失效、下标起点。
  • 边界样例:空容器、单元素、重复值、删除不存在元素。
  • 训练题型:用 桶计数 写一个最小 demo 并压测 1e5 次操作。

卡 22:压位存储#

  • 题目信号:需要 压位存储 支持高频操作。
  • 优先模板:使用 压位存储 的标准 C++17 写法或手写结构。
  • 核心不变量:每次操作后结构内部顺序和计数保持合法。
  • 复杂度目标:通常为 O(1)、O(log n) 或 O(n/word)。
  • 不适用场景:操作需要顺序但使用哈希,或需要删除任意元素但使用堆。
  • 实现检查:检查容器清空、重复元素、迭代器失效、下标起点。
  • 边界样例:空容器、单元素、重复值、删除不存在元素。
  • 训练题型:用 压位存储 写一个最小 demo 并压测 1e5 次操作。

本章赛前默写任务#

  • 默写 vector 邻接表 的模板,并写出 通常为 O(1)、O(log n) 或 O(n/word)。。
  • 默写 链式前向星 的模板,并写出 通常为 O(1)、O(log n) 或 O(n/word)。。
  • 默写 栈模拟递归 的模板,并写出 通常为 O(1)、O(log n) 或 O(n/word)。。
  • 默写 数组队列 BFS 的模板,并写出 通常为 O(1)、O(log n) 或 O(n/word)。。
  • 默写 priority_queue 的模板,并写出 通常为 O(1)、O(log n) 或 O(n/word)。。
  • 默写 set/multiset 的模板,并写出 通常为 O(1)、O(log n) 或 O(n/word)。。
  • 默写 unordered_map 防卡 的模板,并写出 通常为 O(1)、O(log n) 或 O(n/word)。。
  • 默写 bitset 优化 的模板,并写出 通常为 O(1)、O(log n) 或 O(n/word)。。
  • 默写 坐标压缩 的模板,并写出 通常为 O(1)、O(log n) 或 O(n/word)。。
  • 默写 pair 排序 的模板,并写出 通常为 O(1)、O(log n) 或 O(n/word)。。
  • 为本章任选 3 个模板各写一个最小输入。
  • 为本章任选 3 个模板各写一个会使错误实现失败的反例。
  • 把本章所有模板整理到个人代码库,并标注 0-index / 1-index。

02_基础数据结构与语言工具:详细训练清单#

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

训练点 1:vector#

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

训练点 2:array#

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

训练点 3:deque#

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

训练点 4:stack#

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

训练点 5:queue#

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

训练点 6:priority_queue#

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

训练点 7:set#

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

训练点 8:multiset#

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

训练点 9:map#

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

训练点 10:unordered_map#

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

训练点 11:bitset#

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

训练点 12:PBDS#

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

训练点 13:链式前向星#

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

训练点 14:坐标压缩#

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

训练点 15:结构体排序#

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

训练点 16:自定义 hash#

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

训练点 17:内存池#

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

训练点 18:手写队列#

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

训练点 19:vector#

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

训练点 20:array#

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

训练点 21:deque#

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

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

基础数据结构与语言工具
https://lemusakuya.com/posts/study-notes/algorithm/02_基础数据结构与语言工具/
作者
レム・咲く夜
發布於
2026-06-03
許可協議
CC BY-NC-SA 4.0

部分資訊可能已經過時

目錄