C++队列与广度优先搜索(BFS)算法详解

📅 2026/8/11 14:09:58
C++队列与广度优先搜索(BFS)算法详解
1. 队列与广度优先搜索基础概念队列是一种先进先出(FIFO)的线性数据结构在C中通常通过queue标准库来实现。它就像现实生活中的排队场景先来的人先获得服务。队列的两个基本操作是enqueue(入队)在队尾添加元素dequeue(出队)移除队首元素广度优先搜索(BFS)是一种图形遍历算法它从根节点开始先访问所有相邻节点再逐层向外扩展。BFS天然适合用队列来实现因为需要按先发现的节点先访问的顺序处理保证每一层节点完全处理后再进入下一层#include queue using namespace std; queueint q; // 声明整型队列 q.push(1); // 入队 int front q.front(); // 获取队首元素 q.pop(); // 出队2. BFS算法框架与实现标准BFS模板包含以下核心组件void BFS(Node start) { queueNode q; unordered_setNode visited; q.push(start); visited.insert(start); while (!q.empty()) { Node current q.front(); q.pop(); // 处理当前节点 process(current); // 遍历邻居节点 for (Node neighbor : getNeighbors(current)) { if (visited.find(neighbor) visited.end()) { q.push(neighbor); visited.insert(neighbor); } } } }关键点说明使用队列管理待访问节点使用哈希集合记录已访问节点避免重复处理每次从队首取出节点并将其未访问的邻居入队3. 典型应用场景与变种3.1 最短路径问题在无权图中BFS天然可以求解最短路径。例如迷宫最短路径问题struct Point { int x, y; int steps; // 记录步数 }; int shortestPath(vectorvectorint grid, Point start, Point end) { queuePoint q; vectorvectorbool visited(grid.size(), vectorbool(grid[0].size(), false)); q.push(start); visited[start.x][start.y] true; while (!q.empty()) { Point current q.front(); q.pop(); if (current.x end.x current.y end.y) { return current.steps; } // 四个方向移动 int dx[4] {0, 0, 1, -1}; int dy[4] {1, -1, 0, 0}; for (int i 0; i 4; i) { int nx current.x dx[i]; int ny current.y dy[i]; if (nx 0 nx grid.size() ny 0 ny grid[0].size() !visited[nx][ny] grid[nx][ny] ! 0) { q.push({nx, ny, current.steps 1}); visited[nx][ny] true; } } } return -1; // 不可达 }3.2 层级遍历二叉树层级遍历是BFS的经典应用vectorvectorint levelOrder(TreeNode* root) { vectorvectorint result; if (!root) return result; queueTreeNode* q; q.push(root); while (!q.empty()) { int levelSize q.size(); vectorint currentLevel; for (int i 0; i levelSize; i) { TreeNode* node q.front(); q.pop(); currentLevel.push_back(node-val); if (node-left) q.push(node-left); if (node-right) q.push(node-right); } result.push_back(currentLevel); } return result; }3.3 多源BFS当有多个起点时可以初始化队列时加入所有起点queuePoint q; for (auto source : sources) { q.push(source); visited[source.x][source.y] true; }4. 性能优化与常见问题4.1 空间优化技巧原地标记对于矩阵问题可以用特殊值标记已访问省去额外空间双向BFS从起点和终点同时开始搜索相遇时终止// 双向BFS框架 int bidirectionalBFS(Node start, Node end) { queueNode qStart, qEnd; unordered_mapNode, int visitedStart, visitedEnd; qStart.push(start); visitedStart[start] 0; qEnd.push(end); visitedEnd[end] 0; while (!qStart.empty() !qEnd.empty()) { // 交替扩展两个队列 int res expand(qStart, visitedStart, visitedEnd); if (res ! -1) return res; res expand(qEnd, visitedEnd, visitedStart); if (res ! -1) return res; } return -1; }4.2 常见错误排查忘记标记已访问节点导致重复处理和无限循环过早标记应该在节点出队时标记而非入队时队列溢出对于大规模数据考虑使用循环队列边界条件确保正确处理空队列和无效输入5. 高级应用与扩展5.1 优先队列BFS当边有权重时可以使用优先队列实现Dijkstra算法void dijkstra(vectorvectorpairint, int graph, int start) { priority_queuepairint, int, vectorpairint, int, greaterpairint, int pq; vectorint dist(graph.size(), INT_MAX); pq.push({0, start}); dist[start] 0; while (!pq.empty()) { auto [currentDist, u] pq.top(); pq.pop(); if (currentDist dist[u]) continue; for (auto [v, weight] : graph[u]) { if (dist[v] dist[u] weight) { dist[v] dist[u] weight; pq.push({dist[v], v}); } } } }5.2 状态压缩BFS对于状态空间较大的问题可以使用位运算压缩状态struct State { int mask; // 状态压缩 int steps; }; int shortestPathLength(vectorvectorint graph) { int n graph.size(); queueState q; vectorvectorbool visited(n, vectorbool(1 n, false)); for (int i 0; i n; i) { q.push({1 i, 0}); visited[i][1 i] true; } while (!q.empty()) { State current q.front(); q.pop(); if (current.mask (1 n) - 1) { return current.steps; } for (int neighbor : graph[current.node]) { int newMask current.mask | (1 neighbor); if (!visited[neighbor][newMask]) { visited[neighbor][newMask] true; q.push({neighbor, newMask, current.steps 1}); } } } return -1; }6. 工程实践建议使用标准库优先使用std::queue而非手动实现内存管理对于大型图考虑使用内存池并行化对于大规模BFS可以研究并行BFS算法调试技巧打印队列状态和访问记录辅助调试// 调试示例 void debugQueue(queueNode q) { cout Queue state: ; while (!q.empty()) { cout q.front() ; q.pop(); } cout endl; }队列和BFS是算法竞赛和工程开发中的基础工具掌握它们能解决大量实际问题。建议从LeetCode基础题目开始练习逐步过渡到更复杂的应用场景。