拓扑排序(Topological Sort)和关键路径(Critical Path Method, CPM)是图论与项目管理中密切相关的两个重要概念,均基于有向无环图(DAG),且在数据结构中常依托邻接表/邻接矩阵、入度数组、栈/队列及**优先队列(用于关键路径中的最早/最晚时间计算)**等实现。
🔹 拓扑排序
- 适用对象:有向无环图(DAG),反映任务间的偏序关系(如课程先修、编译依赖)。
- 核心思想:按顶点的“依赖顺序”线性排列,使得每条有向边 (u → v) 中 u 总在 v 之前出现。
- 常用算法:Kahn 算法(基于入度的 BFS)或 DFS(记录逆后序)。
- 数据结构支撑:
- 邻接表存储图;
- 入度数组(indegree[])跟踪各顶点入度;
- 队列(Kahn)或递归栈(DFS)维护待处理节点。
🔹 关键路径
- 应用场景:AOE网(Activity On Edge network),即带权有向无环图,边表示活动、权值为持续时间,顶点表示事件(里程碑)。
- 目标:找出从源点到汇点的最长路径(因决定整个工程最短完成时间),该路径上的活动为“关键活动”,无浮动时间。
- 计算步骤:
- 拓扑排序确保事件处理顺序;
- 正向遍历求各事件的最早发生时间 ve[i](最长路径长度);
- 逆向遍历(需逆邻接表或反向图)求各事件的最晚发生时间 vl[i];
- 对每条边 ⟨i, j⟩ 权值 w,若 ve[i] == vl[j] − w,则该活动为关键活动。
- 数据结构需求:
- 邻接表 + 逆邻接表(或存储反向边);
- ve[]、vl[] 数组;
- 辅助栈/队列用于拓扑序列存储与逆序访问。
二者关系:拓扑排序是求解关键路径的前提和基础——只有在 DAG 上完成拓扑排序,才能保证 ve/vl 的动态规划式计算满足依赖顺序。
# Kahn拓扑排序示例(邻接表 + 入度)
from collections import deque
def topological_sort(graph, n):
indegree = [0] * n
for u in range(n):
for v in graph[u]:
indegree[v] += 1
q = deque([i for i in range(n) if indegree[i] == 0])
topo_order = []
while q:
u = q.popleft()
topo_order.append(u)
for v in graph[u]:
indegree[v] -= 1
if indegree[v] == 0:
q.append(v)
return topo_order if len(topo_order) == n else [] # 有环则返回空
利用拓扑排序结果在线性时间内计算 AOE 网中各事件的最早发生时间 ve[i],核心思想是:按拓扑序正向递推,每个事件的 ve 值等于其所有前驱事件的 ve + 对应活动(边)权值的最大值。由于拓扑序保证了“所有前驱已处理”,因此每条边仅被访问一次,总时间复杂度为 O(V + E),即线性。
✅ 具体步骤如下:
-
前提准备:
- 已知 AOE 网的邻接表
graph(graph[u] = [(v, w)]表示从事件 u 到事件 v 的活动,耗时 w); - 已通过 Kahn 或 DFS 得到合法拓扑序列
topo_order(长度为 n,索引 0 → n−1 为执行顺序); - 初始化
ve[0..n−1] = 0(源点 ve[0] 通常设为 0;其余初始为 0,后续更新)。
- 已知 AOE 网的邻接表
-
正向遍历拓扑序列:
对每个事件u(按 topo_order 顺序),遍历其所有出边u → v,执行松弛更新:ve[v] = max(ve[v], ve[u] + w) -
正确性保障:
- 拓扑序中
u出现在v之前 ⇔ 所有指向v的边(u→v)的起点u都已被处理; - 因此
ve[v]在访问到v之前,已由所有前驱充分更新,无需重复迭代(区别于 Bellman-Ford)。
- 拓扑序中
📌 示例(伪代码):
ve = [0] * n # 初始化所有事件最早时间
for u in topo_order: # 按拓扑序处理每个事件
for v, w in graph[u]: # 遍历 u 发出的所有活动
if ve[v] < ve[u] + w: # 松弛:更早完成 v 的可能?
ve[v] = ve[u] + w
⚠️ 注意事项:
- 源点(入度为 0 的唯一起点)的
ve[source] = 0;若存在多个源点,需显式设其ve = 0; - 若图不连通或存在不可达事件,其
ve保持 0(可预先初始化为-∞并设源点为 0,但 AOE 网通常规定单源连通 DAG); - 此过程天然规避环检测——若拓扑排序失败(返回空),则 ve 计算无意义(AOE 网定义要求无环)。
综上,该方法严格依赖拓扑序的偏序性质,是动态规划在 DAG 上的典型线性应用。

645

被折叠的 条评论
为什么被折叠?



