万能头文件
cpp
#include <bits/stdc++.h>
using namespace std;
bits/stdc++.h 包含了绝大多数标准库头文件,在算法竞赛和面试手撕代码时能显著节省书写时间。
注意:这是 GCC 编译器特有的非标准头文件,在 MSVC 或某些严格环境下可能无法使用,但在绝大多数在线笔试平台(牛客、LeetCode C++ 环境通常支持 GCC)中可正常工作。
一、动态数组 vector
声明:
cpp
vector<int> v; // 空数组
vector<int> v(10); // 10 个 0
vector<int> v(10, 5); // 10 个 5
vector<vector<int>> matrix(5, vector<int>(5, 0)); // 5x5 二维数组
vector<int> v = {1, 2, 3}; // 列表初始化
常用操作:
| 操作 | 代码 | 复杂度 |
|---|---|---|
| 尾部添加 | v.push_back(x) | 均摊 O(1) |
| 尾部删除 | v.pop_back() | O(1) |
| 访问元素 | vi 或 v.at(i) | O(1) |
| 首尾迭代器 | v.begin(), v.end() | O(1) |
| 大小/空判断 | v.size(), v.empty() | O(1) |
| 指定位置插入 | v.insert(v.begin()+i, x) | O(n) |
| 指定位置删除 | v.erase(v.begin()+i) | O(n) |
| 调整大小 | v.resize(n) | O(n) |
| 排序 | sort(v.begin(), v.end()) | O(n log n) |
| 二分查找(有序) | binary_search(v.begin(), v.end(), x) | O(log n) |
常用技巧:
cpp
// 去重(需先排序)
sort(v.begin(), v.end());
v.erase(unique(v.begin(), v.end()), v.end());
// 二维数组遍历
for (auto& row : matrix) {
for (int x : row) cout << x << " ";
}
二、栈 stack
声明:
cpp
stack<int> s;
常用操作:
| 操作 | 代码 |
|---|---|
| 入栈 | s.push(x) |
| 出栈 | s.pop()(无返回值) |
| 取栈顶 | s.top() |
| 判空 | s.empty() |
| 大小 | s.size() |
栈适用于括号匹配、逆波兰表达式求值、单调栈等场景。底层默认使用 deque,但是相对来说算是细节,一般不需要关心 。
三、队列 queue
声明:
cpp
queue<int> q;
常用操作:
| 操作 | 代码 |
|---|---|
| 入队 | q.push(x) |
| 出队 | q.pop()(无返回值) |
| 取队首 | q.front() |
| 取队尾 | q.back() |
| 判空/大小 | q.empty(), q.size() |
BFS 的标配容器。如果需要双端操作,使用 deque:
cpp
deque<int> dq;
dq.push_back(x); // 尾插
dq.push_front(x); // 头插 O(1)
dq.pop_back();
dq.pop_front();
四、优先队列 priority_queue
声明:
cpp
priority_queue<int> pq; // 大顶堆(默认)
priority_queue<int, vector<int>, greater<int>> min_pq; // 小顶堆
常用操作:
| 操作 | 代码 |
|---|---|
| 插入 | pq.push(x) |
| 删除堆顶 | pq.pop() |
| 取堆顶 | pq.top() |
| 判空/大小 | pq.empty(), pq.size() |
自定义类型:
cpp
struct Node { int dist; int id; };
struct cmp {
bool operator()(Node& a, Node& b) {
return a.dist > b.dist; // 距离小的优先级高(小顶堆)
}
};
priority_queue<Node, vector<Node>, cmp> pq;
适用于 Dijkstra、Top K 问题、任务调度等 。
五、链表 list
声明:
cpp
list<int> lst;
常用操作:
| 操作 | 代码 |
|---|---|
| 头/尾插入 | lst.push_front(x), lst.push_back(x) |
| 头/尾删除 | lst.pop_front(), lst.pop_back() |
| 指定位置插入 | lst.insert(it, x) |
| 删除元素 | lst.erase(it) |
| 排序 | lst.sort() |
| 判空/大小 | lst.empty(), lst.size() |
list 是双向链表,不支持随机访问 \[\]。插入删除 O(1)(已知迭代器位置),但查找 O(n) 。
手撕链表通常需要手写节点结构,因为面试官考察的是指针操作:
cpp
struct ListNode {
int val;
ListNode* next;
ListNode(int x) : val(x), next(nullptr) {}
};
六、树(set / map:平衡二叉搜索树)
STL 没有直接提供"树"容器,但 set 和 map 底层是红黑树(平衡 BST),提供 O(log n) 的有序操作。
set(有序集合,去重)
声明:
cpp
set<int> s;
常用操作:
cpp
s.insert(x); // 插入 O(log n)
s.erase(x); // 删除
s.find(x); // 查找,返回迭代器
s.count(x); // 存在返回 1
s.lower_bound(x); // >= x 的第一个元素
s.upper_bound(x); // > x 的第一个元素
map(有序键值对)
声明:
cpp
map<string, int> mp;
常用操作:
cpp
mp["key"] = 1; // 插入/修改
mp.insert({"key", 1});
mp.erase("key");
mp.find("key"); // 返回迭代器
mp.count("key"); // 判断存在
for (auto& [k, v] : mp) // C++17 结构化绑定遍历
cout << k << ": " << v;
手写二叉树节点
cpp
struct TreeNode {
int val;
TreeNode *left, *right;
TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};
七、哈希表 unordered_map / unordered_set
声明:
cpp
unordered_map<int, int> hashmap; // 键值对
unordered_set<int> hashset; // 集合
常用操作(与 map/set 类似,但无序):
| 操作 | 代码 | 平均复杂度 |
|---|---|---|
| 插入/修改 | mpkey = val | O(1) |
| 查找 | mp.find(key) | O(1) |
| 存在判断 | mp.count(key) | O(1) |
| 删除 | mp.erase(key) | O(1) |
关键区别:
unordered_map 平均 O(1),但最坏 O(n)(哈希冲突),且无序
map 稳定 O(log n),有序,可用 lower_bound
面试中涉及"查找/计数/去重"且不要求顺序时,优先使用 unordered_map 或 unordered_set 。
八、图(邻接表表示)
C++ STL 没有内置图容器,通常用 vector 模拟邻接表:
cpp
// 邻接表:g[u] 存储 u 的所有邻居
vector<vector<int>> g(n + 1);
// 添加有向边 u -> v
g[u].push_back(v);
// 添加无向边
g[u].push_back(v);
g[v].push_back(u);
// 带权图:pair<邻点, 权重>
vector<vector<pair<int, int>>> wg(n + 1);
wg[u].push_back({v, w});
BFS 遍历模板:
cpp
queue<int> q;
vector<bool> visited(n + 1, false);
q.push(start);
visited[start] = true;
while (!q.empty()) {
int u = q.front(); q.pop();
for (int v : g[u]) {
if (!visited[v]) {
visited[v] = true;
q.push(v);
}
}
}
DFS 递归模板:
cpp
void dfs(int u, vector<vector<int>>& g, vector<bool>& visited) {
visited[u] = true;
for (int v : g[u]) {
if (!visited[v]) dfs(v, g, visited);
}
}
九、C++17 结构化绑定(强烈推荐)
在遍历 map 或 vector<pair> 时,结构化绑定可以大幅简化代码:
cpp
// 以前
for (auto& it : mp) {
cout << it.first << " " << it.second;
}
// C++17
for (auto& [key, value] : mp) {
cout << key << " " << value;
}
配合 auto&& 使用可以避免不必要的拷贝。