xxx 有向无环图(DAG)中的拓扑排序算法
字数 1217 2025-11-18 01:58:16

xxx 有向无环图(DAG)中的拓扑排序算法

问题描述
给定一个有向无环图(DAG),要求输出一个顶点的线性序列,使得对于图中任意一条有向边 \(u \to v\),在序列中顶点 \(u\) 都出现在顶点 \(v\) 的前面。这种序列称为拓扑排序(Topological Sorting)。拓扑排序常用于任务调度、依赖关系分析等场景。


解题过程

1. 理解拓扑排序的核心性质

  • 有向无环图(DAG):图中不存在任何环,否则拓扑排序无法进行。
  • 偏序关系:边 \(u \to v\) 表示 \(u\) 必须先于 \(v\) 出现,但不相连的顶点间顺序任意。
  • 入度(In-Degree):指向某顶点的边的数量。拓扑排序通常从入度为 0 的顶点开始。

2. 算法选择:Kahn 算法(基于 BFS)

Kahn 算法通过维护入度为 0 的顶点集合,逐步移除顶点并更新相邻顶点的入度,最终得到拓扑序列。

步骤详解

  1. 初始化

    • 计算每个顶点的入度,存入数组 indegree[]
    • 将所有入度为 0 的顶点加入队列(或栈)。
  2. 处理顶点

    • 从队列中取出一个顶点 \(u\),将其加入拓扑序列。
    • 遍历 \(u\) 的所有邻接顶点 \(v\)
      • \(v\) 的入度减 1。
      • \(v\) 的入度变为 0,将 \(v\) 加入队列。
    • 重复直到队列为空。
  3. 检测环

    • 若拓扑序列长度不等于顶点总数,说明图中存在环,无法进行拓扑排序。

3. 示例演示

考虑以下 DAG(顶点 0~4,边为 0→1, 0→2, 1→3, 2→3, 3→4):

0 → 1 → 3 → 4
 ↘  2 ↗

逐步执行

  1. 初始入度:[0,1,1,2,1](顶点 0 入度为 0,其他顶点入度非零)。
  2. 队列初始化为 [0]
  3. 取出 0:
    • 序列:[0]
    • 更新 1 的入度→0,2 的入度→0,队列变为 [1,2]
  4. 取出 1:
    • 序列:[0,1]
    • 更新 3 的入度→1,队列为 [2]
  5. 取出 2:
    • 序列:[0,1,2]
    • 更新 3 的入度→0,队列为 [3]
  6. 取出 3:
    • 序列:[0,1,2,3]
    • 更新 4 的入度→0,队列为 [4]
  7. 取出 4:
    • 序列:[0,1,2,3,4](完成)。

4. 关键细节

  • 时间复杂度\(O(V+E)\),其中 \(V\) 为顶点数,\(E\) 为边数。
  • 空间复杂度\(O(V)\)(存储入度和队列)。
  • 多解情况:当队列中存在多个入度为 0 的顶点时,任意选择顺序均合法(例如示例中 1 和 2 可互换)。
  • 环检测:若最终序列长度不足 \(V\),说明剩余顶点均存在环依赖。

5. 应用场景

  • 课程安排:前置课程必须在后续课程之前完成。
  • 编译顺序:源文件间的依赖关系。
  • 工作流调度:任务间的先后约束。

通过以上步骤,可以系统地求解任何 DAG 的拓扑排序,并确保依赖关系的正确性。

xxx 有向无环图(DAG)中的拓扑排序算法 问题描述 给定一个有向无环图(DAG),要求输出一个顶点的线性序列,使得对于图中任意一条有向边 \( u \to v \),在序列中顶点 \( u \) 都出现在顶点 \( v \) 的前面。这种序列称为拓扑排序(Topological Sorting)。拓扑排序常用于任务调度、依赖关系分析等场景。 解题过程 1. 理解拓扑排序的核心性质 有向无环图(DAG) :图中不存在任何环,否则拓扑排序无法进行。 偏序关系 :边 \( u \to v \) 表示 \( u \) 必须先于 \( v \) 出现,但不相连的顶点间顺序任意。 入度(In-Degree) :指向某顶点的边的数量。拓扑排序通常从入度为 0 的顶点开始。 2. 算法选择:Kahn 算法(基于 BFS) Kahn 算法通过维护入度为 0 的顶点集合,逐步移除顶点并更新相邻顶点的入度,最终得到拓扑序列。 步骤详解 : 初始化 : 计算每个顶点的入度,存入数组 indegree[] 。 将所有入度为 0 的顶点加入队列(或栈)。 处理顶点 : 从队列中取出一个顶点 \( u \),将其加入拓扑序列。 遍历 \( u \) 的所有邻接顶点 \( v \): 将 \( v \) 的入度减 1。 若 \( v \) 的入度变为 0,将 \( v \) 加入队列。 重复直到队列为空。 检测环 : 若拓扑序列长度不等于顶点总数,说明图中存在环,无法进行拓扑排序。 3. 示例演示 考虑以下 DAG(顶点 0~4,边为 0→1, 0→2, 1→3, 2→3, 3→4): 逐步执行 : 初始入度: [0,1,1,2,1] (顶点 0 入度为 0,其他顶点入度非零)。 队列初始化为 [0] 。 取出 0: 序列: [0] 更新 1 的入度→0,2 的入度→0,队列变为 [1,2] 。 取出 1: 序列: [0,1] 更新 3 的入度→1,队列为 [2] 。 取出 2: 序列: [0,1,2] 更新 3 的入度→0,队列为 [3] 。 取出 3: 序列: [0,1,2,3] 更新 4 的入度→0,队列为 [4] 。 取出 4: 序列: [0,1,2,3,4] (完成)。 4. 关键细节 时间复杂度 :\(O(V+E)\),其中 \(V\) 为顶点数,\(E\) 为边数。 空间复杂度 :\(O(V)\)(存储入度和队列)。 多解情况 :当队列中存在多个入度为 0 的顶点时,任意选择顺序均合法(例如示例中 1 和 2 可互换)。 环检测 :若最终序列长度不足 \(V\),说明剩余顶点均存在环依赖。 5. 应用场景 课程安排:前置课程必须在后续课程之前完成。 编译顺序:源文件间的依赖关系。 工作流调度:任务间的先后约束。 通过以上步骤,可以系统地求解任何 DAG 的拓扑排序,并确保依赖关系的正确性。