图的遍历算法
字数 2551 2025-10-29 11:32:31

图的遍历算法

好的,我们开始学习“图的遍历算法”。图的遍历是指按照某种规则,系统地访问图中的每一个顶点,并且每个顶点只被访问一次的过程。这是图论中最基础、最重要的算法类别之一,是解决许多图论问题的前提。

第一步:理解遍历的必要性与核心思想

想象一下,你进入了一个巨大的、房间之间由走廊连接的网络迷宫(图),你的任务是走遍每一个房间(顶点),但又不希望重复进入同一个房间。你需要一个系统性的策略来完成这个任务。这就是遍历算法要解决的问题。

遍历的核心思想是:

  1. 避免重复:确保每个顶点只被访问一次。
  2. 避免遗漏:确保所有顶点都被访问到。
  3. 记录状态:需要记录哪些顶点已经被访问过(通常使用一个名为 visited 的数组或集合来实现)。

根据访问顶点的“策略”不同,主要分为两种经典的遍历算法:广度优先搜索和深度优先搜索。虽然它们是基础,但我们将深入探讨其实现细节、性质和应用。

第二步:广度优先搜索

广度优先搜索的策略是“由近及远”,类似于水波扩散或社交网络中好友层次的探索。

1. 算法过程详解:

  • 初始化:从一个选定的起始顶点 s 开始。将 s 标记为“已访问”,并将其放入一个队列中。队列是一种“先进先出”的数据结构。
  • 循环过程:只要队列不为空,就重复以下步骤:
    1. 出队:从队列的头部取出一个顶点,我们称它为当前顶点 u
    2. 访问:正式地“访问”这个顶点 u(例如,打印其值或进行其他计算)。
    3. 探索邻域:检查 u 的所有未被访问过的邻接顶点。对于每一个这样的邻接顶点 v
      • v 标记为“已访问”。
      • v 入队(放入队列的尾部)。

2. 关键性质与示例:

  • 生成树:BFS 遍历过程中经过的边会构成一棵以起始顶点为根的树,称为 BFS 生成树
  • 最短路径:在无权图中,BFS 能天然地找出从起始顶点 s 到所有其他可达顶点的最短路径(即边数最少的路径)。在 BFS 生成树中,从根 s 到任意顶点 v 的路径就是最短路径。
  • 层级关系:顶点是按照距离起始顶点的边数(层级)由小到大依次被访问的。

假设有一个简单的图:A-B-C,A-D。从A开始BFS:

  1. 访问A,队列:[A]。
  2. 出队A,访问A的未访邻居B和D。标记并入队B、D。队列变为:[B, D]。
  3. 出队B,访问B。B的未访邻居是C。标记并入队C。队列变为:[D, C]。
  4. 出队D,访问D。D没有未访邻居。队列变为:[C]。
  5. 出队C,访问C。C没有未访邻居。队列空,结束。
    访问顺序:A, B, D, C。

第三步:深度优先搜索

深度优先搜索的策略是“一路走到黑,再回头”,类似于走迷宫时沿着一条路一直走下去,直到死胡同再返回上一个岔路口。

1. 算法过程详解(递归版本,最直观):

  • 初始化:从一个起始顶点 s 开始。
  • 递归函数 DFS(u)
    1. 将顶点 u 标记为“已访问”。
    2. 访问顶点 u
    3. 遍历 u 的所有邻接顶点 v
      • 如果 v 未被访问过,则递归地调用 DFS(v)

2. 算法过程详解(迭代版本,使用栈):

  • 初始化:从起始顶点 s 开始。将 s 标记为“已访问”,并将其压入一个。栈是一种“后进先出”的数据结构。
  • 循环过程:只要栈不为空,就重复以下步骤:
    1. 弹栈:从栈顶弹出一个顶点,作为当前顶点 u
    2. 访问:访问顶点 u
    3. 探索邻域:检查 u 的所有未被访问过的邻接顶点。对于每一个这样的邻接顶点 v
      • v 标记为“已访问”。
      • v 压栈

3. 关键性质与示例:

  • 生成森林:DFS 遍历过程中经过的边会构成一棵树(如果图连通)或一个森林(多棵不相交的树),称为 DFS 生成森林
  • 时间戳:可以记录每个顶点被第一次访问(发现)的时间和完成对其所有邻居探索的时间。这对分析图的结构(如判断环、拓扑排序)非常有用。
  • 分类边:在 DFS 生成森林中,边可以被分类为:
    • 树边:遍历时经过的边,是生成森林的一部分。
    • 回边:指向祖先顶点的边(可用于检测环)。
    • 前向边:指向后代顶点的非树边。
    • 交叉边:连接不同 DFS 树或同一棵树中无直系祖先关系的顶点的边(在无向图的DFS中不会出现交叉边)。

同样以图 A-B-C,A-D 为例,从A开始DFS(递归):

  1. 访问A,递归进入A的第一个邻居B。
  2. 访问B,递归进入B的邻居C。
  3. 访问C,C无未访邻居,递归返回到B。
  4. B无其他未访邻居,递归返回到A。
  5. A的下一个未访邻居是D,递归进入D。
  6. 访问D,D无未访邻居,递归返回到A。结束。
    访问顺序:A, B, C, D。

第四步:对比与应用场景

特性 广度优先搜索 深度优先搜索
数据结构 队列 栈(递归调用栈或显式栈)
遍历顺序 按距离起始点的层级 沿分支深入到底再回溯
空间复杂度 最坏情况 O( V
经典应用 无权图的最短路径、社交网络中的“度”分离、网络广播 检测环拓扑排序寻找连通分量路径查找(不保证最短)

第五步:进阶概念与变体

掌握了基本的BFS和DFS后,可以理解一些重要的变体和扩展:

  1. 连通分量:对于无向图,从一个未访问顶点开始执行一次BFS或DFS,所能访问到的所有顶点集合构成一个连通分量。重复此过程直到所有顶点被访问,即可找出图的所有连通分量。
  2. 双向BFS:当需要寻找两个特定顶点之间的最短路径时,可以同时从起点和终点开始BFS。当两个搜索的“波前”相遇时,路径即被找到。这在搜索空间巨大时能显著减少探索的顶点数量。
  3. 迭代深化DFS:结合了BFS和DFS的优点。它通过逐步增加深度限制来反复运行DFS,既具备了DFS的空间效率,又能像BFS一样按层搜索,常用于人工智能的搜索问题。

图的遍历算法是图论的基石,几乎所有复杂的图算法都在其基础上构建。理解它们的细微差别和适用场景至关重要。

图的遍历算法 好的,我们开始学习“图的遍历算法”。图的遍历是指按照某种规则,系统地访问图中的每一个顶点,并且每个顶点只被访问一次的过程。这是图论中最基础、最重要的算法类别之一,是解决许多图论问题的前提。 第一步:理解遍历的必要性与核心思想 想象一下,你进入了一个巨大的、房间之间由走廊连接的网络迷宫(图),你的任务是走遍每一个房间(顶点),但又不希望重复进入同一个房间。你需要一个系统性的策略来完成这个任务。这就是遍历算法要解决的问题。 遍历的核心思想是: 避免重复 :确保每个顶点只被访问一次。 避免遗漏 :确保所有顶点都被访问到。 记录状态 :需要记录哪些顶点已经被访问过(通常使用一个名为 visited 的数组或集合来实现)。 根据访问顶点的“策略”不同,主要分为两种经典的遍历算法:广度优先搜索和深度优先搜索。虽然它们是基础,但我们将深入探讨其实现细节、性质和应用。 第二步:广度优先搜索 广度优先搜索的策略是“由近及远”,类似于水波扩散或社交网络中好友层次的探索。 1. 算法过程详解: 初始化 :从一个选定的起始顶点 s 开始。将 s 标记为“已访问”,并将其放入一个 队列 中。队列是一种“先进先出”的数据结构。 循环过程 :只要队列不为空,就重复以下步骤: 出队 :从队列的头部取出一个顶点,我们称它为当前顶点 u 。 访问 :正式地“访问”这个顶点 u (例如,打印其值或进行其他计算)。 探索邻域 :检查 u 的所有 未被访问过的 邻接顶点。对于每一个这样的邻接顶点 v : 将 v 标记为“已访问”。 将 v 入队 (放入队列的尾部)。 2. 关键性质与示例: 生成树 :BFS 遍历过程中经过的边会构成一棵以起始顶点为根的树,称为 BFS 生成树 。 最短路径 :在 无权图 中,BFS 能天然地找出从起始顶点 s 到所有其他可达顶点的 最短路径 (即边数最少的路径)。在 BFS 生成树中,从根 s 到任意顶点 v 的路径就是最短路径。 层级关系 :顶点是按照距离起始顶点的边数(层级)由小到大依次被访问的。 假设有一个简单的图:A-B-C,A-D。从A开始BFS: 访问A,队列:[ A ]。 出队A,访问A的未访邻居B和D。标记并入队B、D。队列变为:[ B, D ]。 出队B,访问B。B的未访邻居是C。标记并入队C。队列变为:[ D, C ]。 出队D,访问D。D没有未访邻居。队列变为:[ C ]。 出队C,访问C。C没有未访邻居。队列空,结束。 访问顺序:A, B, D, C。 第三步:深度优先搜索 深度优先搜索的策略是“一路走到黑,再回头”,类似于走迷宫时沿着一条路一直走下去,直到死胡同再返回上一个岔路口。 1. 算法过程详解(递归版本,最直观): 初始化 :从一个起始顶点 s 开始。 递归函数 DFS(u) : 将顶点 u 标记为“已访问”。 访问 顶点 u 。 遍历 u 的所有邻接顶点 v : 如果 v 未被访问过,则递归地调用 DFS(v) 。 2. 算法过程详解(迭代版本,使用栈): 初始化 :从起始顶点 s 开始。将 s 标记为“已访问”,并将其压入一个 栈 。栈是一种“后进先出”的数据结构。 循环过程 :只要栈不为空,就重复以下步骤: 弹栈 :从栈顶弹出一个顶点,作为当前顶点 u 。 访问 :访问顶点 u 。 探索邻域 :检查 u 的所有 未被访问过的 邻接顶点。对于每一个这样的邻接顶点 v : 将 v 标记为“已访问”。 将 v 压栈 。 3. 关键性质与示例: 生成森林 :DFS 遍历过程中经过的边会构成一棵树(如果图连通)或一个森林(多棵不相交的树),称为 DFS 生成森林 。 时间戳 :可以记录每个顶点被第一次访问(发现)的时间和完成对其所有邻居探索的时间。这对分析图的结构(如判断环、拓扑排序)非常有用。 分类边 :在 DFS 生成森林中,边可以被分类为: 树边 :遍历时经过的边,是生成森林的一部分。 回边 :指向祖先顶点的边(可用于检测环)。 前向边 :指向后代顶点的非树边。 交叉边 :连接不同 DFS 树或同一棵树中无直系祖先关系的顶点的边(在无向图的DFS中不会出现交叉边)。 同样以图 A-B-C,A-D 为例,从A开始DFS(递归): 访问A,递归进入A的第一个邻居B。 访问B,递归进入B的邻居C。 访问C,C无未访邻居,递归返回到B。 B无其他未访邻居,递归返回到A。 A的下一个未访邻居是D,递归进入D。 访问D,D无未访邻居,递归返回到A。结束。 访问顺序:A, B, C, D。 第四步:对比与应用场景 | 特性 | 广度优先搜索 | 深度优先搜索 | | :--- | :--- | :--- | | 数据结构 | 队列 | 栈(递归调用栈或显式栈) | | 遍历顺序 | 按距离起始点的层级 | 沿分支深入到底再回溯 | | 空间复杂度 | 最坏情况 O(|V|) | 最坏情况 O(|V|)(由递归深度决定) | | 经典应用 | 无权图的最短路径 、社交网络中的“度”分离、网络广播 | 检测环 、 拓扑排序 、 寻找连通分量 、 路径查找 (不保证最短) | 第五步:进阶概念与变体 掌握了基本的BFS和DFS后,可以理解一些重要的变体和扩展: 连通分量 :对于无向图,从一个未访问顶点开始执行一次BFS或DFS,所能访问到的所有顶点集合构成一个 连通分量 。重复此过程直到所有顶点被访问,即可找出图的所有连通分量。 双向BFS :当需要寻找两个特定顶点之间的最短路径时,可以同时从起点和终点开始BFS。当两个搜索的“波前”相遇时,路径即被找到。这在搜索空间巨大时能显著减少探索的顶点数量。 迭代深化DFS :结合了BFS和DFS的优点。它通过逐步增加深度限制来反复运行DFS,既具备了DFS的空间效率,又能像BFS一样按层搜索,常用于人工智能的搜索问题。 图的遍历算法是图论的基石,几乎所有复杂的图算法都在其基础上构建。理解它们的细微差别和适用场景至关重要。