应使用 std::queue 实现 BFS,因其底层基于 std::deque,支持 O(1) 入队出队且无需手动管理内存;需在入队前检查越界与访问合法性,用独立 vector 标记已访问,推荐用 emplace 和方向常量优化性能。

用 std::queue 实现 BFS,别手写队列
标准库的 std::queue 是最稳妥选择,它底层默认用 std::deque,支持 O(1) 的入队出队,且不用手动管理内存。自己用数组或链表模拟队列,容易在边界(比如空队列取 front())崩掉,还可能漏清空队列导致后续测试复现失败。
常见错误现象:std::queue::front() 或 pop() 在空队列上调用 → 未定义行为,Debug 模式下可能断言失败,Release 下直接读垃圾内存。
- 初始化时用
std::queue<:pair int>></:pair>存坐标,比存指针或自定义结构体更轻量 - 进队前必须检查越界和访问合法性(比如是否是障碍、是否已访问过),否则重复入队拖慢速度甚至死循环
- 标记已访问的位置建议用独立的
std::vector<:vector>></:vector>,别依赖节点状态字段——BFS 中节点只进队一次,但状态字段若被多处修改,逻辑容易混乱
邻接点遍历顺序影响结果,但不影响连通性判断
BFS 本身不保证“字典序”或“顺时针”出队,只是按入队时间先后处理。如果你在网格中上下左右四个方向扩展,顺序写成 {-1,0}, {0,1}, {1,0}, {0,-1} 还是反过来,对是否能到达终点毫无影响,但会影响第一次到达某点的路径长度(这个长度始终是最短的)和具体走哪条最短路。
性能上无差异;兼容性也没问题——所有 C++ 标准都保证 std::queue 的 FIFO 行为。
立即学习“C++免费学习笔记(深入)”;
- 方向数组推荐封装成常量:
const std::vector<:pair int>> dirs = {{-1,0},{0,1},{1,0},{0,-1}};</:pair> - 避免在循环里反复构造临时
std::pair,直接用queue.emplace(r + dr, c + dc)减少拷贝 - 如果图是邻接表形式(
std::vector<:vector>></:vector>),就遍历graph[u],不用方向数组
遇到 std::queue 报错 “no matching function for call to …” 怎么办
这通常是因为你试图把不能默认构造或不可拷贝的类型塞进 std::queue。比如用了含引用成员、含 const 成员,或者自定义类没写移动构造函数,在 C++11 以后编译器会尝试移动语义,失败就报这个错。
典型场景:想存一个带父节点指针的结构体用于回溯路径,结果编译不过。
- 优先改用值语义:比如存坐标
int r, c而不是Node* - 如果真要存复杂对象,确保它满足
MoveConstructible(有移动构造函数,或编译器自动生成) - 别用
std::queue<:unique_ptr>></:unique_ptr>——std::unique_ptr不可拷贝,但可以移动;这时必须用emplace或push(std::move(ptr)),不能push(ptr)
二维数组索引越界是 BFS 最常崩的点
尤其在网格题里,r + dr 算出来是 -1 或等于 height,直接当数组下标用,就是未定义行为。ASan(AddressSanitizer)能抓到,但线上环境不会提醒你。
别依赖“题目保证数据合法”——实际工程或 OJ 多组测试中,一组数据错了,后面全乱。
- 检查必须写在进队前:
if (nr >= 0 && nr = 0 && nc - 把行列检查逻辑抽成内联函数,比如
inline bool valid(int r, int c) { return r >= 0 && r = 0 && c ,减少重复和漏写 - 如果用
std::vector存网格,千万别混用grid[r][c]和grid.at(r).at(c)——后者抛异常,前者不检查,崩溃了都不知道哪一行
visited 数组翻转节奏。









