queue - 队列
std::queue 是基于先进先出 (FIFO) 原则的容器适配器,默认底层使用 deque 实现。在算法竞赛中,队列是实现广度优先搜索 (BFS) 的核心工具。
竞赛中的核心考点
| 操作 | 时间复杂度 | 竞赛要点 |
|---|
| 入队 (push) | O(1) | 向队尾添加元素 |
| 出队 (pop) | O(1) | 从队首移除元素 |
| 访问队首 (front) | O(1) | 获取队首元素 |
| 访问队尾 (back) | O(1) | 获取队尾元素 |
| 判空 (empty) | O(1) | 检查队列是否为空 |
| 大小 (size) | O(1) | 获取队列大小 |
一、基本用法
1.1 初始化
#include <bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
signed main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
queue<int> q;
queue<int, list<int>> q_list;
queue<int, deque<int>> q_deque;
return 0;
}
1.2 基本操作
#include <bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
signed main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
queue<int> q;
q.push(10);
q.push(20);
q.push(30);
cout << "队首: " << q.front() << endl;
cout << "队尾: " << q.back() << endl;
q.pop();
cout << "出队后队首: " << q.front() << endl;
cout << "队列大小: " << q.size() << endl;
cout << "队列是否为空: " << (q.empty() ? "是" : "否") << endl;
while (!q.empty())
{
q.pop();
}
cout << "清空后队列是否为空: " << (q.empty() ? "是" : "否") << endl;
return 0;
}
二、BFS 算法实现
2.1 基本 BFS
#include <bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
void bfs(int start, const vector<vector<int>>& adj)
{
int n = adj.size();
vector<bool> visited(n, false);
queue<int> q;
q.push(start);
visited[start] = true;
while (!q.empty())
{
int u = q.front();
q.pop();
cout << u << " ";
for (int v : adj[u])
{
if (!visited[v])
{
visited[v] = true;
q.push(v);
}
}
}
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n = 6;
vector<vector<int>> adj(n);
adj[0] = {1, 2};
adj[1] = {0, 3, 4};
adj[2] = {0, 5};
adj[3] = {1};
adj[4] = {1};
adj[5] = {2};
cout << "BFS 遍历结果: ";
bfs(0, adj);
cout << endl;
return 0;
}
2.2 BFS 求最短路径
#include <bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
vector<int> shortest_path(int start, int end, const vector<vector<int>>& adj)
{
int n = adj.size();
vector<int> dist(n, -1);
vector<int> parent(n, -1);
queue<int> q;
q.push(start);
dist[start] = 0;
while (!q.empty())
{
int u = q.front();
q.pop();
if (u == end)
{
break;
}
for (int v : adj[u])
{
if (dist[v] == -1)
{
dist[v] = dist[u] + 1;
parent[v] = u;
q.push(v);
}
}
}
vector<int> path;
if (dist[end] == -1)
{
return path;
}
for (int v = end; v != -1; v = parent[v])
{
path.push_back(v);
}
reverse(path.begin(), path.end());
return path;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n = 6;
vector<vector<int>> adj(n);
adj[0] = {1, 2};
adj[1] = {0, 3, 4};
adj[2] = {0, 5};
adj[3] = {1};
adj[4] = {1};
adj[5] = {2};
vector<int> path = shortest_path(0, 5, adj);
cout << "最短路径: ";
for (int v : path)
{
cout << v << " ";
}
cout << endl;
return 0;
}
三、竞赛实战应用
3.1 层序遍历
struct TreeNode
{
int val;
TreeNode* left;
TreeNode* right;
TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};
vector<vector<int>> level_order(TreeNode* root)
{
vector<vector<int>> result;
if (!root)
{
return result;
}
queue<TreeNode*> q;
q.push(root);
while (!q.empty())
{
int level_size = q.size();
vector<int> level;
for (int i = 0; i < level_size; i++)
{
TreeNode* node = q.front();
q.pop();
level.push_back(node->val);
if (node->left)
{
q.push(node->left);
}
if (node->right)
{
q.push(node->right);
}
}
result.push_back(level);
}
return result;
}
3.2 多源 BFS
vector<vector<int>> multi_source_bfs(int n, int m, const vector<pair<int, int>>& sources)
{
vector<vector<int>> dist(n, vector<int>(m, -1));
queue<pair<int, int>> q;
for (const auto& source : sources)
{
int x = source.first;
int y = source.second;
dist[x][y] = 0;
q.emplace(x, y);
}
int dx[] = {-1, 1, 0, 0};
int dy[] = {0, 0, -1, 1};
while (!q.empty())
{
pair<int, int> front = q.front();
int x = front.first;
int y = front.second;
q.pop();
for (int i = 0; i < 4; i++)
{
int nx = x + dx[i];
int ny = y + dy[i];
if (nx >= 0 && nx < n && ny >= 0 && ny < m && dist[nx][ny] == -1)
{
dist[nx][ny] = dist[x][y] + 1;
q.emplace(nx, ny);
}
}
}
return dist;
}
3.3 0-1 BFS
vector<int> zero_one_bfs(int start, const vector<vector<pair<int, int>>>& adj)
{
int n = adj.size();
vector<int> dist(n, INT_MAX);
deque<int> dq;
dist[start] = 0;
dq.push_front(start);
while (!dq.empty())
{
int u = dq.front();
dq.pop_front();
for (const auto& edge : adj[u])
{
int v = edge.first;
int w = edge.second;
if (dist[v] > dist[u] + w)
{
dist[v] = dist[u] + w;
if (w == 0)
{
dq.push_front(v);
}
else
{
dq.push_back(v);
}
}
}
}
return dist;
}
四、C++11 现代特性
4.1 使用 pair 元素访问
#include <bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
signed main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
queue<pair<int, int>> q;
q.emplace(1, 2);
q.emplace(3, 4);
while (!q.empty())
{
pair<int, int> front = q.front();
int x = front.first;
int y = front.second;
cout << "x: " << x << ", y: " << y << endl;
q.pop();
}
return 0;
}
4.2 使用列表初始化
#include <bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
signed main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
deque<int> d = {1, 2, 3, 4, 5};
queue<int> q(d);
while (!q.empty())
{
cout << q.front() << " ";
q.pop();
}
return 0;
}
五、性能优化
5.1 预分配空间
queue<int> q;
int expected_size = 100000;
deque<int> d;
d.reserve(expected_size);
queue<int> q(d);
5.2 避免不必要的拷贝
void process_queue(queue<int> q)
{
}
void process_queue(queue<int>& q)
{
}
queue<int> create_queue()
{
queue<int> q;
return q;
}
5.3 合理选择底层容器
| 底层容器 | 优点 | 缺点 | 适用场景 |
|---|
| deque(默认) | 两端操作高效 | 内存碎片 | 一般 BFS |
| list | 内存分配灵活 | 缓存不友好 | 频繁插入删除 |
| vector | 缓存友好 | 需要手动管理大小 | 固定大小的队列 |
六、常见错误与注意事项
| 错误 | 说明 | 解决方案 |
|---|
| 访问空队列 | 未定义行为 | 先检查 empty() |
| BFS 中重复访问 | 导致无限循环 | 使用 visited 数组标记 |
| 内存使用过高 | 队列过大 | 考虑剪枝或优化算法 |
| 效率问题 | 频繁的 push/pop | 选择合适的底层容器 |
| 路径重建错误 | 父节点指针未正确设置 | 确保每个节点都记录父节点 |
七、C++11 完整竞赛模板
#include <bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
void bfs(int start, const vector<vector<int>>& adj)
{
int n = adj.size();
vector<bool> visited(n, false);
queue<int> q;
q.push(start);
visited[start] = true;
while (!q.empty())
{
int u = q.front();
q.pop();
cout << u << " ";
for (int v : adj[u])
{
if (!visited[v])
{
visited[v] = true;
q.push(v);
}
}
}
cout << endl;
}
vector<int> shortest_path(int start, int end, const vector<vector<int>>& adj)
{
int n = adj.size();
vector<int> dist(n, -1);
vector<int> parent(n, -1);
queue<int> q;
q.push(start);
dist[start] = 0;
while (!q.empty())
{
int u = q.front();
q.pop();
if (u == end)
{
break;
}
for (int v : adj[u])
{
if (dist[v] == -1)
{
dist[v] = dist[u] + 1;
parent[v] = u;
q.push(v);
}
}
}
vector<int> path;
if (dist[end] == -1)
{
return path;
}
for (int v = end; v != -1; v = parent[v])
{
path.push_back(v);
}
reverse(path.begin(), path.end());
return path;
}
vector<vector<int>> multi_source_bfs(int n, int m, const vector<pair<int, int>>& sources)
{
vector<vector<int>> dist(n, vector<int>(m, -1));
queue<pair<int, int>> q;
for (const auto& source : sources)
{
int x = source.first;
int y = source.second;
dist[x][y] = 0;
q.emplace(x, y);
}
int dx[] = {-1, 1, 0, 0};
int dy[] = {0, 0, -1, 1};
while (!q.empty())
{
pair<int, int> front = q.front();
int x = front.first;
int y = front.second;
q.pop();
for (int i = 0; i < 4; i++)
{
int nx = x + dx[i];
int ny = y + dy[i];
if (nx >= 0 && nx < n && ny >= 0 && ny < m && dist[nx][ny] == -1)
{
dist[nx][ny] = dist[x][y] + 1;
q.emplace(nx, ny);
}
}
}
return dist;
}
vector<int> zero_one_bfs(int start, const vector<vector<pair<int, int>>>& adj)
{
int n = adj.size();
vector<int> dist(n, INT_MAX);
deque<int> dq;
dist[start] = 0;
dq.push_front(start);
while (!dq.empty())
{
int u = dq.front();
dq.pop_front();
for (const auto& edge : adj[u])
{
int v = edge.first;
int w = edge.second;
if (dist[v] > dist[u] + w)
{
dist[v] = dist[u] + w;
if (w == 0)
{
dq.push_front(v);
}
else
{
dq.push_back(v);
}
}
}
}
return dist;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n = 6;
vector<vector<int>> adj(n);
adj[0] = {1, 2};
adj[1] = {0, 3, 4};
adj[2] = {0, 5};
adj[3] = {1};
adj[4] = {1};
adj[5] = {2};
cout << "BFS 遍历: ";
bfs(0, adj);
vector<int> path = shortest_path(0, 5, adj);
cout << "最短路径: ";
for (int v : path)
{
cout << v << " ";
}
cout << endl;
int rows = 3, cols = 3;
vector<pair<int, int>> sources = {{0, 0}, {2, 2}};
vector<vector<int>> dist = multi_source_bfs(rows, cols, sources);
cout << "多源 BFS 距离矩阵: " << endl;
for (int i = 0; i < rows; i++)
{
for (int j = 0; j < cols; j++)
{
cout << dist[i][j] << " ";
}
cout << endl;
}
return 0;
}
总结:竞赛要点
- FIFO 特性:队列的先进先出特性是 BFS 的基础
- BFS 算法:队列是实现 BFS 的标准工具
- 层序遍历:适用于树和图的层序访问
- 最短路径:在无权图中求最短路径
- 多源 BFS:处理多个起点的情况
- 0-1 BFS:处理边权为 0 或 1 的图
- 性能优化:选择合适的底层容器,避免不必要的拷贝
- 现代 C++11:使用 auto 类型推导、范围 for 循环和 emplace 原地构造
- 边界处理:注意队列判空,避免访问空队列
- 实战应用:图遍历、迷宫求解、网络流等
queue 是算法竞赛中解决广度优先搜索问题的核心工具,掌握它的使用技巧能让你在处理图论和搜索问题时更加得心应手。