本文最后更新于 34 天前,其中的信息可能已经有所发展或是发生改变。
通用前提
所有容器使用前需包含对应头文件,核心规则:
- 判空优先:访问容器元素(如栈顶、队头)前,必须用
empty()判断是否为空,避免程序崩溃; - 效率优先:优先用
unordered_*(哈希实现,O (1) 效率),仅需排序时换map/set(红黑树,O (logn)); - 核心接口:每个容器只需记 5 个左右核心用法,无需死记所有接口。
第一部分:stack(栈)—— 先进后出(FILO)
一、核心定位
蓝桥杯高频必考,专门处理 “后进先出” 场景,比如 DFS 搜索、括号匹配、表达式求值,是算法题的 “回溯工具”。
二、基础知识点
1. 定义方式(需包含头文件#include <stack>)
cpp
运行
// 定义存储int类型的栈,可替换为string/char/结构体等
stack<int> st;
// 示例:存储字符串的栈
stack<string> st_str;
2. 核心接口(仅需记这 5 个,按使用频率排序)
| 接口 | 功能 | 注意点 |
|---|---|---|
st.push(x) | 入栈:把元素 x 添加到栈顶 | 无返回值,仅添加 |
st.pop() | 出栈:删除栈顶元素 | 无返回值!仅删除,不返回元素 |
st.top() | 取栈顶元素:返回栈顶的值 | 必须先判空(!st.empty()),否则崩溃 |
st.empty() | 判断栈是否为空 | 返回 bool(空 = true,非空 = false),访问 top 前必用 |
st.size() | 获取栈内元素个数 | 返回整数,偶尔用于统计 |
3. 核心特性
- 只能访问栈顶元素,无法访问中间 / 栈底元素(“封死” 的容器,仅能操作顶部);
- 所有操作都是对栈顶的 “增 / 删 / 查”,符合 “先进后出”(比如先压 1、2、3,取的时候先取 3)。
三、实战案例(蓝桥杯高频:括号匹配)
题目场景
判断输入的字符串中的括号是否合法(仅包含()/[]/{}),合法条件:
- 左括号必须用相同类型的右括号闭合;
- 左括号必须按正确顺序闭合(如
([)]不合法)。
完整代码(可直接运行)
cpp
运行
#include <iostream>
#include <stack>
#include <string>
using namespace std;
// 判断括号是否匹配
bool isMatch(char left, char right) {
if ((left == '(' && right == ')') ||
(left == '[' && right == ']') ||
(left == '{' && right == '}')) {
return true;
}
return false;
}
int main() {
string s;
cout << "请输入括号字符串:";
cin >> s;
stack<char> st; // 存储左括号的栈
bool isValid = true; // 标记是否合法
for (char c : s) { // 遍历每个字符
// 左括号:入栈
if (c == '(' || c == '[' || c == '{') {
st.push(c);
}
// 右括号:匹配栈顶左括号
else {
// 无左括号匹配 → 非法
if (st.empty()) {
isValid = false;
break;
}
// 取出栈顶左括号,判断是否匹配
char top_char = st.top();
st.pop(); // 匹配成功则出栈
if (!isMatch(top_char, c)) {
isValid = false;
break;
}
}
}
// 最后栈必须为空(所有左括号都匹配完成)
if (isValid && st.empty()) {
cout << "括号合法!" << endl;
} else {
cout << "括号非法!" << endl;
}
return 0;
}
运行结果示例
plaintext
// 示例1:合法
请输入括号字符串:()[]{}
括号合法!
// 示例2:非法(顺序错误)
请输入括号字符串:([)]
括号非法!
// 示例3:非法(左括号多余)
请输入括号字符串:((()))]
括号非法!
竞赛技巧
- 栈是处理 “嵌套 / 回溯” 问题的核心,比如表达式求值、DFS 手动实现(递归栈溢出时用栈替代);
- 记住 “左括号入栈,右括号匹配出栈” 的核心逻辑,可解决所有括号类问题。
第二部分:queue(队列)—— 先进先出(FIFO)
一、核心定位
蓝桥杯顶级高频,BFS(广度优先搜索)的 “标配工具”,解决迷宫、最短路径、层序遍历等问题,是搜索题的 “通关钥匙”。
二、基础知识点
1. 定义方式(需包含头文件#include <queue>)
cpp
运行
// 定义存储int类型的队列
queue<int> q;
// 示例:存储结构体(比如迷宫的坐标)
struct Pos { int x, y; };
queue<Pos> q_pos;
2. 核心接口(仅需记这 6 个)
| 接口 | 功能 | 注意点 |
|---|---|---|
q.push(x) | 入队:把元素 x 添加到队列尾部 | 无返回值 |
q.pop() | 出队:删除队列头部元素 | 无返回值!仅删除 |
q.front() | 取队头元素:返回队列第一个元素 | 必须先判空(!q.empty()) |
q.back() | 取队尾元素:返回队列最后一个元素 | 偶尔用,非核心 |
q.empty() | 判断队列是否为空 | 访问 front 前必用 |
q.size() | 获取队列元素个数 | 层序遍历(BFS)时统计层数可用 |
3. 核心特性
- 只能访问队头 / 队尾,无法访问中间元素;
- 符合 “先进先出”(比如先入 1、2、3,取的时候先取 1),适合 “顺序处理” 场景。
三、实战案例(蓝桥杯经典:BFS 走迷宫)
题目场景
迷宫为 5×5 的二维数组,0表示可走,1表示墙,从起点(0,0)走到终点(4,4),求最短步数(BFS 是求最短路径的最优解)。
完整代码(可直接运行)
cpp
运行
#include <iostream>
#include <queue>
using namespace std;
// 迷宫大小
const int N = 5;
// 迷宫地图(0=可走,1=墙)
int maze[N][N] = {
{0, 1, 0, 0, 0},
{0, 1, 0, 1, 0},
{0, 0, 0, 1, 0},
{0, 1, 1, 1, 0},
{0, 0, 0, 0, 0}
};
// 方向:上下左右(BFS核心,遍历四个方向)
int dx[4] = {-1, 1, 0, 0};
int dy[4] = {0, 0, -1, 1};
// 记录是否访问过(避免重复走)
bool visited[N][N] = {false};
// 存储坐标和步数的结构体
struct Node {
int x, y; // 坐标
int step; // 到该位置的步数
};
int main() {
queue<Node> q; // BFS的核心队列
// 起点入队:(0,0),步数0
q.push({0, 0, 0});
visited[0][0] = true; // 标记已访问
int min_step = -1; // 最短步数,默认-1(无解)
// BFS核心循环:队列不为空则继续
while (!q.empty()) {
// 取出队头元素(当前位置)
Node cur = q.front();
q.pop(); // 出队
// 到达终点:记录步数并退出
if (cur.x == N-1 && cur.y == N-1) {
min_step = cur.step;
break;
}
// 遍历四个方向
for (int i = 0; i < 4; i++) {
int nx = cur.x + dx[i];
int ny = cur.y + dy[i];
// 条件1:坐标在迷宫范围内
// 条件2:不是墙
// 条件3:未被访问过
if (nx >= 0 && nx < N && ny >=0 && ny < N
&& maze[nx][ny] == 0 && !visited[nx][ny]) {
// 新位置入队,步数+1
q.push({nx, ny, cur.step + 1});
visited[nx][ny] = true; // 标记已访问
}
}
}
if (min_step != -1) {
cout << "走到终点的最短步数:" << min_step << endl;
} else {
cout << "无法到达终点!" << endl;
}
return 0;
}
运行结果
plaintext
走到终点的最短步数:8
竞赛技巧
- BFS 的核心逻辑:“队头出队→处理当前节点→邻接节点入队”,必须用 queue 实现;
- 记住 “方向数组 + 访问标记 + 队列存坐标 / 步数” 的组合,可解决所有迷宫 / 最短路径问题;
- queue 的
front()和pop()必须成对使用(取完队头就出队,避免重复处理)。
第三部分:unordered_map(哈希映射)—— 键值对存储
一、核心定位
蓝桥杯高频,解决 “统计频率、哈希映射、快速查找” 问题,比如统计字符 / 数字出现次数、记录键值关联关系。
二、基础知识点
1. 定义方式(需包含头文件#include <unordered_map>)
cpp
运行
// 定义:unordered_map<键类型, 值类型> 变量名
unordered_map<string, int> mp; // 字符串为键,整数为值(统计字符频率)
unordered_map<int, bool> mp_vis; // 整数为键,布尔值为值(标记是否访问)
2. 核心接口(仅需记这 6 个)
| 接口 | 功能 | 注意点 |
|---|---|---|
mp[key] = value | 赋值:给键 key 绑定值 value | 核心!最常用,key 不存在则自动创建,值默认初始化(int=0,bool=false) |
mp.find(key) | 查找:找键 key 是否存在 | 返回迭代器,存在则指向该键值对,不存在则返回mp.end() |
mp.erase(key) | 删除:删除键 key 对应的键值对 | 无返回值,key 不存在则无操作 |
mp.empty() | 判断是否为空 | 返回 bool |
mp.size() | 获取键值对个数 | 统计不同键的数量 |
mp.count(key) | 统计 key 出现次数 | 仅返回 0/1(因为 key 唯一),等价于find判存在 |
3. 核心特性
- 键(key)唯一:同一个键只能存一个值,重复赋值会覆盖;
- 哈希实现:查找 / 赋值效率 O (1),比
map快,竞赛优先用; - 无序存储:键的顺序不固定,若需排序则换
map。
三、实战案例(蓝桥杯高频:统计字符频率)
题目场景
输入一个字符串,统计每个字符出现的次数,并按字符输出(比如输入abac,输出a:2, b:1, c:1)。
完整代码(可直接运行)
cpp
运行
#include <iostream>
#include <unordered_map>
#include <string>
using namespace std;
int main() {
string s;
cout << "请输入字符串:";
cin >> s;
unordered_map<char, int> mp; // 键=字符,值=出现次数
// 统计频率:遍历每个字符
for (char c : s) {
mp[c]++; // 核心!key不存在则默认0,++后变为1;存在则累加
}
// 遍历输出结果(遍历unordered_map)
cout << "字符频率统计:" << endl;
for (auto& pair : mp) { // pair.first=键(字符),pair.second=值(次数)
cout << pair.first << ": " << pair.second << endl;
}
// 单独查询某个字符的频率
char target;
cout << "请输入要查询的字符:";
cin >> target;
if (mp.find(target) != mp.end()) { // 判断字符是否存在
cout << target << " 出现次数:" << mp[target] << endl;
} else {
cout << target << " 未出现!" << endl;
}
return 0;
}
运行结果示例
plaintext
请输入字符串:bluecup123123
字符频率统计:
3: 2
2: 2
1: 2
p: 1
u: 1
c: 1
e: 1
u: 1
l: 1
b: 1
请输入要查询的字符:1
1 出现次数:2
竞赛技巧
mp[key]++是统计频率的 “万能写法”,无需判断 key 是否存在;- 判存在优先用
mp.find(key) != mp.end(),比mp.count(key)更直观; - 需排序时(比如按字符顺序输出),把
unordered_map换成map即可(用法完全一致)。
第四部分:unordered_set(哈希集合)—— 去重 + 快速判存
一、核心定位
蓝桥杯高频,解决 “自动去重、快速判断元素是否存在” 问题,比如数组去重、判断元素是否访问过。
二、基础知识点
1. 定义方式(需包含头文件#include <unordered_set>)
cpp
运行
// 定义:unordered_set<元素类型> 变量名
unordered_set<int> st; // 存储整数的集合
unordered_set<string> st_str; // 存储字符串的集合
2. 核心接口(仅需记这 5 个)
| 接口 | 功能 | 注意点 |
|---|---|---|
st.insert(x) | 插入:添加元素 x 到集合 | 自动去重,重复插入无效果 |
st.find(x) | 查找:判断元素 x 是否存在 | 返回迭代器,存在则指向 x,不存在则返回st.end() |
st.erase(x) | 删除:删除元素 x | 无返回值,x 不存在则无操作 |
st.empty() | 判断是否为空 | 返回 bool |
st.size() | 获取集合元素个数 | 统计不同元素的数量 |
3. 核心特性
- 元素唯一:自动去重,插入重复元素不会报错,但集合中仅存一个;
- 哈希实现:查找 / 插入效率 O (1),竞赛优先用;
- 无序存储:元素顺序不固定,需排序则换
set。
三、实战案例(蓝桥杯高频:数组去重)
题目场景
输入一个整数数组(长度不限),去除重复元素,输出去重后的数组和不同元素的个数。
完整代码(可直接运行)
cpp
运行
#include <iostream>
#include <unordered_set>
#include <vector>
using namespace std;
int main() {
vector<int> nums = {1, 2, 2, 3, 3, 3, 4, 5, 5}; // 测试数组
unordered_set<int> st; // 用于去重的集合
// 数组去重:插入集合(自动去重)
for (int num : nums) {
st.insert(num);
}
// 输出去重后的元素
cout << "去重后的数组:";
for (int num : st) {
cout << num << " ";
}
cout << endl;
// 输出不同元素个数
cout << "不同元素的个数:" << st.size() << endl;
// 快速判断元素是否存在
int target = 3;
if (st.find(target) != st.end()) {
cout << target << " 存在于数组中!" << endl;
} else {
cout << target << " 不存在!" << endl;
}
return 0;
}
运行结果
plaintext
去重后的数组:1 2 3 4 5
不同元素的个数:5
3 存在于数组中!
竞赛技巧
- 去重的 “懒人写法”:把数组 / 字符串元素全部插入
unordered_set,自动完成去重; - 判存在的核心:
if (st.find(x) != st.end()),比遍历数组快(O (1) vs O (n)); - 需排序时换
set,用法完全一致,仅输出顺序不同。
核心总结(必背)
| 容器 | 核心特性 | 蓝桥杯核心场景 | 最常用接口 |
|---|---|---|---|
| stack | 先进后出 | 括号匹配、DFS、表达式求值 | push/pop/top/empty |
| queue | 先进先出 | BFS、迷宫 / 最短路径、排队模拟 | push/pop/front/empty |
| unordered_map | 键值对、哈希、键唯一 | 统计频率、哈希映射、判存在 | mp[key]=value/find/erase |
| unordered_set | 元素唯一、哈希、去重 | 数组去重、快速判存在 | insert/find/erase |
学习优先级
- 先掌握
queue(BFS 是蓝桥杯搜索题核心); - 再掌握
stack(括号匹配 / DFS); - 最后掌握
unordered_map+unordered_set(统计 / 去重); - 每个容器练 1-2 道对应场景的题,即可完全掌握,无需死记接口。