首页/数据结构/05-graph/图的遍历 🔗 在 Obsidian 中打开
数据结构 · 05-graph

图的遍历

难度 ★★★重要度 ★★★★★ 考查频率 高题型 选择 / 综合应用 数据结构/图DFSBFS深度优先广度优先
速查
DFS 用栈/递归(邻接矩阵 $O(|V|^2)$、邻接表 $O(|V|+|E|)$),BFS 用队列(同复杂度);一次遍历访问一个连通分量,调用次数 = 连通分量数。

速查

项目
主题图的遍历
核心概念从起始顶点出发,访问一个邻接点,再访问其邻接点,直到无法继续,回溯继续
时间复杂度$O(|V|)$、$O(|V|+|E|)$、邻接矩阵 $O(|V|^2)$
难度⭐⭐⭐
重要性⭐⭐⭐⭐⭐

核心概念

一、深度优先搜索(DFS)

算法思想

从起始顶点出发,访问一个邻接点,再访问该邻接点的邻接点,直到无法继续,回溯到上一个顶点继续。

代码实现(邻接矩阵)

bool visited[MaxVertexNum];

void DFS(MGraph G, int v) {
    visit(v);
    visited[v] = true;
    for (int w = 0; w < G.vexnum; w++)
        if (G.edge[v][w] == 1 && !visited[w])
            DFS(G, w);
}

代码实现(邻接表)

void DFS(ALGraph G, int v) {
    visit(v);
    visited[v] = true;
    ArcNode *p = G.vertices[v].first;
    while (p != NULL) {
        if (!visited[p->adjvex])
            DFS(G, p->adjvex);
        p = p->next;
    }
}

遍历整个图(处理非连通图)

void DFSTraverse(MGraph G) {
    for (int i = 0; i < G.vexnum; i++)
        visited[i] = false;
    for (int i = 0; i < G.vexnum; i++)
        if (!visited[i])
            DFS(G, i);
}
存储方式时间复杂度
邻接矩阵$O(|V|^2)$
邻接表$O(|V|+|E|)$

二、广度优先搜索(BFS)

算法思想

从起始顶点出发,先访问所有邻接点,再依次访问邻接点的邻接点。

代码实现(邻接表)

void BFS(ALGraph G, int v) {
    Queue Q; InitQueue(&Q);
    visit(v); visited[v] = true; EnQueue(&Q, v);
    while (!QueueEmpty(Q)) {
        DeQueue(&Q, &v);
        ArcNode *p = G.vertices[v].first;
        while (p != NULL) {
            if (!visited[p->adjvex]) {
                visit(p->adjvex);
                visited[p->adjvex] = true;
                EnQueue(&Q, p->adjvex);
            }
            p = p->next;
        }
    }
}
存储方式时间复杂度
邻接矩阵$O(|V|^2)$
邻接表$O(|V|+|E|)$

三、DFS vs BFS

特性DFSBFS
数据结构栈(递归)队列
搜索策略深度优先广度优先
空间复杂度$O(|V|)$$O(|V|)$
典型应用拓扑排序、连通性无权图最短路径

四、图的连通性

  • 无向图:连通图任意两点可达;连通分量是极大连通子图;一次 DFS/BFS 能访问所有结点 ⟹ 连通图;需多次调用 ⟹ 非连通,次数 = 连通分量数
  • 有向图:强连通图任意两点互相可达;强连通分量是极大强连通子图

手算示例

邻接表:

0 → [1] → [2]
1 → [0] → [3]
2 → [0] → [3]
3 → [1] → [2] → [4]
4 → [3]

例 1:DFS(从顶点 0)

访问序列:0 → 1 → 3 → 2 → 4

例 2:BFS(从顶点 0)

访问序列:0 → 1 → 2 → 3 → 4

例 3:连通分量计数

0 --- 1    3 --- 4

2         5

DFS 调用 3 次,分别访问 {0,1,2}、{3,4}、{5} ⟹ 连通分量数 = 3

常见考法

考法 1:DFS/BFS 序列给出图,写出 DFS 或 BFS 遍历序列(邻接点顺序不同,序列可能不同)。
考法 2:时间复杂度问:邻接表上 DFS 的时间复杂度?答:$O(|V|+|E|)$。
考法 3:连通分量问:如何求连通分量数?答:DFS/BFS 调用次数。
考法 4:DFS vs BFS问:主要区别?答:DFS 用栈(递归),BFS 用队列。

易错点

注意
  1. 邻接点顺序不同,DFS/BFS 序列可能不同
  2. 非连通图需多次调用 DFS/BFS
  3. visited 数组每次遍历前要初始化
  4. BFS 用队列,不是栈
  5. 邻接表 DFS 时间是 $O(|V|+|E|)$,不是 $O(|V|^2)$

核心结论

遍历方式数据结构时间(邻接表)时间(邻接矩阵)
DFS$O(|V|+|E|)$$O(|V|^2)$
BFS队列$O(|V|+|E|)$$O(|V|^2)$

记忆卡片

DFS 用什么数据结构?
栈(通常用递归实现)。
BFS 用什么数据结构?
队列。
邻接表上 DFS 的时间复杂度?
$O(|V|+|E|)$。
如何判断图是否连通?
一次 DFS/BFS 能访问所有结点即连通。
BFS 能求无权图最短路径吗?
能。
非连通图怎么遍历?
每个未访问顶点都发起一次 DFS/BFS,次数 = 连通分量数。

交互动画 · DFS vs BFS(从 0 出发)

顶点 0 出发:橙=已访问,深红=当前访问点,流动线=生成树边 0 1 2 3 4
选择遍历模式后点击「播放」
DFS 深度优先,BFS 广度优先

相关知识点

graph-storage-structure minimum-spanning-tree shortest-path topological-sort critical-path graph-traversal-and-applications

↑ 以上为站内 HTML 相对链接(纯网页可浏览);本页右上「在 Obsidian 中打开」跳回源笔记。