每天学一个算法--拓扑排序(Topological Sort)

📘 教案 13:拓扑排序(Topological Sort)


一、问题背景

在很多实际问题中,我们并不是单纯处理"数值",而是在处理依赖关系

例如:

  • 课程学习:必须先学 A 才能学 B
  • 构建系统:必须先编译库,再编译主程序
  • 任务调度:某些任务必须在其他任务完成后才能开始

这些问题可以抽象为一种结构:

有向图中的依赖关系


二、形式化定义

给定一个有向图 ( G = (V, E) ),其中:

  • ( V ):顶点(任务)
  • ( E ):有向边(依赖关系)

若存在边:

u→v\]\[ u \\to v \]\[u→v

表示:

必须先完成 ( u ),才能执行 ( v )


三、拓扑排序的定义

拓扑排序是:

对有向无环图(DAG)中的节点进行排序,使得所有依赖关系得到满足

换句话说:

如果存在边 ( u \to v ),那么在排序结果中:

u 一定出现在 v 之前\]\[ u \\text{ 一定出现在 } v \\text{ 之前} \]\[u 一定出现在 v 之前


四、关键前提:必须是 DAG


什么是 DAG?

DAG(Directed Acyclic Graph):

有向无环图


为什么必须无环?

如果存在:

text 复制代码
A → B → C → A

那么:

  • A 依赖 B
  • B 依赖 C
  • C 又依赖 A

👉 没有任何一个可以先执行

因此:

只要存在环,就不存在拓扑排序


五、核心思路

拓扑排序的核心是:

不断找到"没有依赖"的节点,然后删除它们


"没有依赖"是什么意思?

即:

入度=0\]\[ \\text{入度} = 0 \]\[入度=0


六、算法一:Kahn 算法(基于 BFS)


核心步骤

  1. 统计每个节点的入度

  2. 将所有入度为 0 的节点加入队列

  3. 依次处理队列:

    • 取出一个节点
    • 将其加入结果
    • 删除它的所有出边
    • 更新邻居节点的入度
  4. 如果有新的入度为 0 的节点,继续加入队列


代码结构

python 复制代码
from collections import deque

def topo_sort(graph):
    indegree = {v: 0 for v in graph}

    # 统计入度
    for u in graph:
        for v in graph[u]:
            indegree[v] += 1

    queue = deque([v for v in graph if indegree[v] == 0])
    result = []

    while queue:
        u = queue.popleft()
        result.append(u)

        for v in graph[u]:
            indegree[v] -= 1
            if indegree[v] == 0:
                queue.append(v)

    if len(result) != len(graph):
        return "存在环"

    return result

七、算法二:DFS 方法


思路

通过 DFS,在"回溯时"记录节点顺序。


关键点

  • 访问完所有子节点后,再加入结果
  • 最终将结果逆序

代码结构

python 复制代码
def topo_sort_dfs(graph):
    visited = set()
    result = []

    def dfs(node):
        if node in visited:
            return
        visited.add(node)

        for nei in graph[node]:
            dfs(nei)

        result.append(node)

    for node in graph:
        dfs(node)

    return result[::-1]

八、两种方法对比

方法 思路 特点
Kahn(BFS) 入度为0开始 易理解,能检测环
DFS 后序遍历 更偏递归结构

九、时间复杂度

O(V+E)\]\[ O(V + E) \]\[O(V+E)

  • 每个节点访问一次
  • 每条边处理一次

十、典型应用


1. 课程安排(经典题)

判断是否可以完成所有课程


2. 构建系统(编译顺序)


3. 任务调度


4. 数据处理依赖


十一、常见错误


  • 忘记判断是否有环(结果长度不等于节点数)
  • 入度更新错误
  • 图结构构建错误

十二、本质总结


拓扑排序解决的问题不是"排序大小",而是:

在一组存在依赖关系的元素中,找到一个合法执行顺序


更适合教学的一句话表达

拓扑排序是在有向无环图中,按照依赖关系对节点进行线性排序,使得所有前置条件都在使用之前被满足。

相关推荐
大龄程序员狗哥2 小时前
第25篇:Q-Learning算法解析——强化学习中的经典“价值”学习(原理解析)
人工智能·学习·算法
exp_add32 小时前
质数相关知识
算法
小辉同志2 小时前
215. 数组中的第K个最大元素
数据结构·算法·leetcode··快速选择
小O的算法实验室3 小时前
2025年IEEE TITS,基于矩阵的进化计算+面向无线传感器网络数据收集无人机路径规划,深度解析+性能实测
算法·论文复现·智能算法·智能算法改进
OidEncoder3 小时前
编码器分辨率与机械精度的关系
人工智能·算法·机器人·自动化
memcpy03 小时前
LeetCode 2615. 等值距离和【相同元素分组+前缀和;考虑距离和的增量】中等
算法·leetcode·职场和发展
炽烈小老头4 小时前
【 每天学习一点算法 2026/04/22】四数相加 II
学习·算法
alphaTao4 小时前
LeetCode 每日一题 2026/4/20-2026/4/26
算法·leetcode·职场和发展
Robot_Nav5 小时前
TD3 —— 双延迟深度确定性策略梯度算法文献解读
算法·td3·drl