拓扑排序(Topological Sort)和关键路径(Critical Path Method, CPM)是图论与项目管理中密切相关的两个重要概念

拓扑排序(Topological Sort)和关键路径(Critical Path Method, CPM)是图论与项目管理中密切相关的两个重要概念,均基于有向无环图(DAG),且在数据结构中常依托邻接表/邻接矩阵入度数组栈/队列及**优先队列(用于关键路径中的最早/最晚时间计算)**等实现。

🔹 拓扑排序

  • 适用对象:有向无环图(DAG),反映任务间的偏序关系(如课程先修、编译依赖)。
  • 核心思想:按顶点的“依赖顺序”线性排列,使得每条有向边 (u → v) 中 u 总在 v 之前出现。
  • 常用算法:Kahn 算法(基于入度的 BFS)或 DFS(记录逆后序)。
  • 数据结构支撑:
    • 邻接表存储图;
    • 入度数组(indegree[])跟踪各顶点入度;
    • 队列(Kahn)或递归栈(DFS)维护待处理节点。

🔹 关键路径

  • 应用场景:AOE网(Activity On Edge network),即带权有向无环图,边表示活动、权值为持续时间,顶点表示事件(里程碑)。
  • 目标:找出从源点到汇点的最长路径(因决定整个工程最短完成时间),该路径上的活动为“关键活动”,无浮动时间。
  • 计算步骤:
    1. 拓扑排序确保事件处理顺序;
    2. 正向遍历求各事件的最早发生时间 ve[i](最长路径长度);
    3. 逆向遍历(需逆邻接表或反向图)求各事件的最晚发生时间 vl[i]
    4. 对每条边 ⟨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),即线性。

✅ 具体步骤如下:

  1. 前提准备

    • 已知 AOE 网的邻接表 graphgraph[u] = [(v, w)] 表示从事件 u 到事件 v 的活动,耗时 w);
    • 已通过 Kahn 或 DFS 得到合法拓扑序列 topo_order(长度为 n,索引 0 → n−1 为执行顺序);
    • 初始化 ve[0..n−1] = 0(源点 ve[0] 通常设为 0;其余初始为 0,后续更新)。
  2. 正向遍历拓扑序列
    对每个事件 u(按 topo_order 顺序),遍历其所有出边 u → v,执行松弛更新:

    ve[v] = max(ve[v], ve[u] + w)
    
  3. 正确性保障

    • 拓扑序中 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 上的典型线性应用。

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Bol5261

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值