目录
[二、广度优先搜索(BFS)------ 仿树的层序遍历](#二、广度优先搜索(BFS)—— 仿树的层序遍历)
[1. 核心思想](#1. 核心思想)
[2. 实现步骤(依赖队列)](#2. 实现步骤(依赖队列))
[3. 示例(无向图)](#3. 示例(无向图))
[4. 关键特性](#4. 关键特性)
[5. BFS 与树的层序遍历的比较](#5. BFS 与树的层序遍历的比较)
[6. BFS算法效率分析](#6. BFS算法效率分析)
[7. DFS与BFS之间的比较](#7. DFS与BFS之间的比较)
图的存储结构------图的遍历
二、广度优先搜索(BFS)------ 仿树的层序遍历

图的广度优先搜索(Breadth-First Search, BFS)是一种按 "层次扩散" 方式遍历图的算法,核心是从起始顶点出发,先访问其所有直接邻接顶点(第一层),再依次访问这些邻接顶点的未访问邻接顶点(第二层),以此类推,直至所有顶点被访问。由于其 "逐层访问" 的特性,BFS 与树的层序遍历(按层次访问节点)在逻辑上高度相似,但因图的复杂性(可能有环、多路径)存在关键差异。
1. 核心思想
"先近后远":从起始顶点开始,优先访问距离最近的顶点(边数最少),再按距离递增顺序访问更远的顶点,确保每个顶点被访问一次。
2. 实现步骤(依赖队列)
- 步骤 1:初始化一个队列,将起始顶点标记为 "已访问" 并入队。
- 步骤 2 :当队列非空时,出队一个顶点
u,遍历其所有未访问的邻接顶点w:- 标记
w为 "已访问",访问w(如输出值)。 - 将
w入队(保证后续按层次访问其邻接顶点)。
- 标记
- 步骤 3:重复步骤 2,直至队列为空(所有可达顶点均被访问)。
- 补充:若图为非连通图,需对每个未访问顶点重复上述过程,以覆盖所有连通分量。
3. 示例(无向图)
图结构:顶点 {0,1,2,3,4},边 (0,1), (0,2), (1,3), (1,4), (2,4),从顶点 0 出发的 BFS 过程:
初始:队列=[0],visited=[0:True, 1:False, 2:False, 3:False, 4:False]
1. 出队0 → 访问0 → 遍历邻接顶点1、2(均未访问)→ 标记1、2为True,入队 → 队列=[1,2]
2. 出队1 → 访问1 → 遍历邻接顶点0(已访问)、3、4(3未访问,4未访问)→ 标记3、4为True,入队 → 队列=[2,3,4]
3. 出队2 → 访问2 → 遍历邻接顶点0(已访问)、4(已访问)→ 无新顶点入队 → 队列=[3,4]
4. 出队3 → 访问3 → 遍历邻接顶点1(已访问)→ 无新顶点入队 → 队列=[4]
5. 出队4 → 访问4 → 遍历邻接顶点1(已访问)、2(已访问)→ 队列空,结束。
遍历序列:0 → 1 → 2 → 3 → 4
例题:

算法:
cpp
BFS1(List,n,v) //List为邻接表,v 为起点,Q[n]为辅助队列
{
visit(v);
visited[v]=1; //访问顶点 v 并修改标记
front=n-1;
rear=0; //队列指针初始化
q[rear]=v; //起始点入队
while(rear != front) //队不空时
{
front=(front+1)%n;
v=q[front]; //访问过的顶点出队
q=List[v].firstarc; //p指向第1个邻接点
while(!p) //未到表尾,且邻接域未访问过,则先输出再改标记,最后再入队
{
if(!visited[adjvex(p)])
{
visited(adjvex(p));
visited[adjvex(p)]=1;
rear=(rear+1)%n;
q[rear]=adjvex(p);
}
p=nextarc(p);
}
}
return; //指向单链表中下一个邻接点
}
4. 关键特性
- 队列是核心:确保顶点按 "入队顺序"(即层次顺序)被访问。
- 访问标记不可少 :图可能有环(如顶点 4 连接 1 和 2),
visited数组避免重复访问。 - 时间复杂度:O(n+e)(n 为顶点数,e 为边数),与存储结构相关(邻接表遍历邻接顶点更快)。
- 最短路径特性:在无权图中,BFS 得到的顶点访问顺序对应 "从起始顶点到该顶点的最短路径(边数最少)"。
- 广度优先搜索是一种分层的搜索过程,每向前走一步可能访问一批顶点,不像DFS那样有回退的情况。因此BFS不是递归过程,其算法也不是递归的。
5. BFS 与树的层序遍历的比较
(1) 相同点(核心逻辑一致)
-
层次访问:均按 "距离起始点的远近" 逐层访问节点,先访问第 k 层节点,再访问第 k+1 层节点。
-
依赖队列:均通过队列实现 "先进先出" 的访问顺序,确保层次顺序不被打乱。
-
遍历结果:在无环结构中(如树),两者遍历序列完全一致(见示例)。
**示例:**树的层序遍历与对应图的 BFS二叉树结构:
0 / \ 1 2 / \ 3 4层序遍历序列:
0 → 1 → 2 → 3 → 4。将树视为无环图(边为父子关系),从 0 出发的 BFS 序列相同。
(2) 不同点(因图的复杂性导致)
| 对比维度 | 图的 BFS | 树的层序遍历 |
|---|---|---|
| 环的处理 | 必须用 visited 数组标记已访问顶点,否则会因环导致无限循环(如顶点 4→1→4)。 |
无需标记(树无环,子节点仅由父节点指向,不会重复访问)。 |
| "父节点" 的唯一性 | 图中顶点可能有多个 "前驱"(如示例中顶点 4 的前驱是 1 和 2),BFS 需处理多路径。 | 树中节点有唯一父节点,层序遍历只需按 "父→子" 顺序访问,无多路径问题。 |
| 连通性 | 若图非连通,需对每个未访问顶点执行 BFS(覆盖所有连通分量)。 | 树是连通结构,从根节点出发一次遍历即可覆盖所有节点。 |
| 适用场景扩展 | 除遍历外,可用于无权图最短路径、网络爬虫(按链接层次抓取)、社交网络好友推荐(一度、二度好友)等。 | 主要用于树的层次化访问(如打印二叉树的层、求树的深度)。 |
图的 BFS 是树的层序遍历在 "带环、多路径、非连通" 结构上的扩展:
- 继承了 "逐层访问" 和 "队列依赖" 的核心逻辑,因此两者在无环连通结构(如树)上表现一致。
- 增加了 "访问标记" 和 "多连通分量处理" 机制,以应对图的复杂性。
6. BFS算法效率分析
设图中有 n 个顶点,e 条边:
(1)如果使用邻接表来表示图,则BFS循环的总时间代价为 ,其中的
是顶点 i 的度
(2)如果使用邻接矩阵,则BFS对于每一个被访问到的顶点,都要遵循检测矩阵中的整整一行(n 个元素),总的时间代价为O()
7. DFS与BFS之间的比较
(1)空间复杂度相同,都是O(n)(借用了堆栈或队列)
(2)时间复杂度只与存储结构(邻接矩阵或邻接表)有关,而与搜索路径无关