(c)广度优先搜索

官方信息技术老师·11 页·深入(追求细节与边界)·0 次浏览·3 天前
图论BFS队列最短路径

(c)广度优先搜索

看清 BFS 层序推进与队列操作,掌握无权图最短路径的求解逻辑与边界处理

按 空格/→ 演示下一步

1 / 11 页

全部页面点击任意一页,跳回舞台从这页播放

图论BFS队列最短路径

(c)广度优先搜索

看清 BFS 层序推进与队列操作,掌握无权图最短路径的求解逻辑与边界处理

1第 1 页 · (c)广度优先搜索

化繁为简

走进迷宫想找出口,与其沿墙试探,不如从入口像水波一样一圈圈往外扩——先被淹到的一定是最近的点。这就是 BFS 的核心直觉。

层序遍历
从起点出发,按与起点距离由近及远,逐层访问所有可达节点
队列驱动
用 FIFO 队列保存待访问节点,先入先出对应先近后远
访问标记
节点入队时立即标记已访问,防止有环图中重复入队
最短路径
无权图中首次到达即最短路径,天然解决最少步数问题
水波纹一圈圈向外扩散对应 →广度优先搜索

波纹同一圈内的点距起点同样远,BFS 按层推进与此一致

2第 2 页 · 化繁为简

策略

策略:定义、要点与典型应用

策略
策略:定义、要点与典型应用
3第 3 页 · 策略

实现

策略想清楚了,落到代码其实就三件事:用对容器、做好标记、循环里按规矩取放。

队列是骨架
先进先出保证先处理完当前层再进入下一层
标记不可或缺
无向图里一条边被两端各看一次,没标记就死循环
取-判-压三步循环
从队首取出,扩展邻居,未访问的入队尾
代价 O(V+E)
V 个顶点 E 条边各访问一次,队列最坏装下所有顶点
水面扔石头波纹一圈圈外扩对应 →BFS 从起点一层层向外扩展

第一圈完全扩散才到第二圈,队列 FIFO 正对应此顺序

T=O(V+E),S=O(V)T = O(|V| + |E|),\quad S = O(|V|)
4第 4 页 · 实现

可能情况

BFS 一层层往外走,但每处理一个节点都可能撞上不同局面——分清这几种情况,是写出正确实现的前提。

首次发现
邻居未被访问,标记并入队——这是 BFS 保证最短性的关键
重复命中
邻居已被标记,意味着已有更短路径到达,直接跳过
命中目标
首次发现目标即得到最短距离,可提前返回节省开销
队列耗尽
所有可达节点都搜过仍无目标,说明目标不可达
迷宫里一扇扇推门对应 →BFS 处理节点的几种情况

新房间标记去过的绕开找到出口就停全搜完仍没找到则目标不存在

5第 5 页 · 可能情况

实例

上一页我们写出了 BFS 的骨架——队列、FIFO、逐层推进。这一页反过来看:哪些问题该第一时间想到 BFS?

无权图最短路径
边权均为 1 时,BFS 首次到达某点就是最少边数路径
树层序遍历
按层从左到右访问节点,对应队列一层的逐出顺序
连通块计数
一次 BFS 标记一个连通分量,可用于岛屿数、朋友圈等
状态空间最短解
八数码、华容道等,每步代价相同,BFS 找最少步数
石头落入水面,涟漪一圈圈扩散对应 →BFS 从起点向外一层层扩展

每扩散一圈 = 队列完成一层的标志,对应无权图最短路径

6第 6 页 · 实例

多连通

上页用一个完整实例跑通了 BFS,但那个图本身是一整块连通区域。现实中的图常常不止一块——地图上有被海洋隔开的多个岛屿,社交网络里有互不来往的几个小圈子。一个起点够不够?

多连通图
至少两个互不相连的连通分量,节点之间没有路径互通
单次 BFS 局限
从任意节点开始只能踏遍它所在的那一块
多起点 BFS
外层循环遍历所有节点,对每个未访问节点各跑一次 BFS
复杂度与计数
每节点每边仍只访问一次 O(V+E),还能顺便数分量个数
群岛探险对应 →多连通 BFS

每座孤岛都得从某点单独出发派船探,主岛探完不会自动跳到邻岛

7第 7 页 · 多连通

复杂度

上一页我们一步步看 BFS 把图搜完。但要落地就躲不开一个问题——'这玩意儿要跑多久,占多大内存?'这一页,咱们把账彻底算清楚。

时间复杂度 O(V+E)
邻接表下,每个顶点入队出队各一次 (V),每条边被检查一次 (E),二者相加
空间复杂度 O(V)
主要被 visited 数组和队列占用,队列中同时存在的节点 ≤ 总节点数
邻接矩阵变 O(V²)
求邻居时需扫一整行 V 个位置,V 个顶点各做一次 ⇒ 共 V²
不连通要调多次
一次 BFS 只覆盖起点可达的部分,剩余孤岛得各起炉灶
投石入水,涟漪向四周扩散对应 →BFS 遍历所有可达节点

每经过一片水面 = V,每跨过一条水渠 = E,各自只发生一次

vVdeg(v)=2E\sum_{v \in V}\deg(v) = 2|E|
8第 8 页 · 复杂度

最短路径

你从家到公司,想找耗时最短的路线——是换乘最少,还是过路口最少?这种「边数最少」的问题,正是 BFS 的拿手好戏。

定义
无权图中,路径长度=经过的边数,最短路径即边数最少的路径
BFS天然契合
逐层扩展,首次到达某节点时的层数就是它的最短距离
前驱回溯
记录每个节点的前一个是谁,到终点后倒着拼出完整路径
典型应用
地铁最少换乘、社交几度分隔、走迷宫、网络跳数
边界
仅限无权图;带权图请交给 Dijkstra 等算法
池塘扔石子看水波对应 →BFS求最短路径

水波一圈圈同时外扩,最先抵达对岸的就是最短距离

d(v)=d(u)+1(仅适用于无权图)d(v) = d(u) + 1 \quad (\text{仅适用于无权图})
9第 9 页 · 最短路径

本节要点

  • FIFO队列是BFS按层推进的内在节律
  • BFS天然解决无权图最短路问题
  • visited缺失会让BFS在带环图上崩溃
  • 时空复杂度同为O(V+E),与DFS同阶
  • 入队-出队-扩展构成可迁移的通用骨架
延伸主题:加权最短路:Dijkstra双向BFS加速0-1 BFS处理0/1边权
10第 10 页 · 本节要点

课后思考

先独立想 5 分钟再对照参考答案。三问覆盖:核心机制、最短距离实现、图的全局遍历。

1为什么 BFS 第一次到达某点时,走过的步数一定是最短的?换个角度看:DFS 为何做不到这一点?

参考答案因为 BFS 按层推进:第一次到达即最短——任何绕远的路径都得跨更多层,步数只可能更多或相等。DFS 一路走到黑,撞到的第一条路径往往不是最短。

2在迷宫求最短出口长度上,如何让 BFS 同时记下'每个房间距入口多少步'?提示:之前实现里哪个数组稍作改造就能复用?

参考答案把 visited 数组升级成 distance:进入邻接点时执行 distance[neighbor] = distance[cur] + 1,每个顶点的最短距离就同步被记下。

3如果图有多个连通块,从一个起点跑 BFS 会漏掉其他块的顶点。想一想:能否只跑一次 BFS 就'摸清'整张图?

参考答案可以。外层加循环:每次 BFS 跑完(队列空),若仍有未访问顶点,就从中再选一个起点继续。调用次数刚好等于连通块个数。

11第 11 页 · 课后思考
(c)广度优先搜索 · 知识图解