双向 BFS

01 / 一句话本质

从两端同时跑 BFS。两侧边界一旦相遇,最短路即得。 优势随深度指数级增长:O(b^d) 变为 O(b^(d/2)) —— 只要源点与目标点都已知,就有效。
题目无权图上源到目标的最短路径长度输入graph=mesh, A → F
AS:0BCDEFT:0
A(正向)和 F(反向)同时跑 BFS。初始化 distS[A] = 0distT[F] = 0,两边的边界各只有一个种子。
0 / 19
边界 |S|、|T|
1, 1
已访问
1 + 1
路径长度
0 / 19

02 / 模式骨架

# 层层推进的双向 BFSdistS { s: 0 }; distT { t: 0 }frontS {s}; frontT {t}while frontS 与 frontT 均非空:if |frontS| ≤ |frontT|: 用 frontS 探查 distTelse: 用 frontT 探查 distSfor u in front for v in adj[u]:if v 已在对侧: return same[u] + other[v] + 1if v 不在同侧: same[v] = same[u] + 1; nextFront ← vreturn −1 # 不连通

03 / 什么时候用这个模式

"最短变换 / 突变"
单词接龙、基因突变、转盘锁 —— 任何起点和终点都是具体状态、且边等代价(每跳一步)的状态空间搜索。
"两端都已知"
只有目标点是具体状态时才有用。 对一对多最短路(Dijkstra、源点 BFS),单侧 BFS 已足够,渐近同阶。
"分支因子大得让 BFS 吃不消"
单词接龙 26 字母 × L 位置,4 轮转盘锁 10 数字 —— 分支因子 b 看起来很吓人。双向 BFS 把深度减半,b^(d/2) 就可接受了。
"无权图最短路"
无权图 BFS 已最短;双向只加额外价值。带权图请用双向 Dijkstra(更高级,面试少考)。

04 / 常见坑

先扩展更大的那一侧。
关键就是让两侧边界平衡 —— 始终先展开较小的。 若一直展开"先到的那侧",一边会爆,加速消失。
更新之后才查对侧、而不是更新之前。
"相遇"检查每次探查邻居时都做 —— 在加入自己访问集合之前就看对侧。 否则可能错过第一次相遇,报出比真正最短更长的路径。
答案公式写反。
u → v 上相遇(u 在当前侧,v 已在对侧), 答案是 distSide[u] + distOther[v] + 1 ——+1 是中间这条桥。Off-by-one 是最经典的 bug。
把第一次相遇当成最短。
展开同一层时,可能有多个相遇候选出现 —— 若关心路径还原,处理完整层再下结论; 否则当前 BFS 不变量保证"该深度第一次相遇"即最短。