ruye07

导航

拓扑排序 学习笔记

拓扑排序 学习笔记

学习日期: 2026-05-12
前置知识: BFS + 队列 + 入度概念


问题场景

有 n 门课,每门课有先修要求。请排出上课顺序,让每门课的预修课都在前面。

微积分(1) → 线代(2) → 概率论(4)
    ↓                    ↓
 高数(3)  ───────────→  机器学习(6)
                          ↑
                       计概(5)

核心思想

入度 = 被几条边指着。

入度为 0 的点 → 没有前置依赖 → 可以直接上

完整过程图解

初始:
  课:   1   2   3   4   5   6
 入度:  0   1   1   1   0   2

 入度为0 → 1和5 → 入队

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

第1步: q = [1, 5]

  弹出 1: 删掉 1→2, 1→3
    2的入度 1→0 ✓ 入队
    3的入度 1→0 ✓ 入队

  弹出 5: 删掉 5→6
    6的入度 2→1

  order = [1, 5]
  q = [2, 3]
  入度:  0   0   0   1   0   1

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

第2步: q = [2, 3]

  弹出 2: 删掉 2→4
    4的入度 1→0 ✓ 入队

  弹出 3: 删掉 3→6
    6的入度 1→0 ✓ 入队

  order = [1, 5, 2, 3]
  q = [4, 6]

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

第3步: q = [4, 6]

  弹出 4: 删掉 4→6(6入度已经是0,不变)
  弹出 6: 无出边

  order = [1, 5, 2, 3, 4, 6]
  q = []

  len(order) = 6 == n → 排序成功 ✓

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

结果: 1 5 2 3 4 6  (多种答案均可)

验证每条边是否从左指向右:
  1→2 ✓ 1→3 ✓ 2→4 ✓ 3→6 ✓ 4→6 ✓ 5→6 ✓

环检测

若存在环,如 2→4 且 4→2:

  初始: 2入度=1, 4入度=1
  第1步处理后: 2入度=1, 4入度=1  ← 永远≥1

  环上的点永远进不了队列!
  q 为空 → while 退出 → len(order) < n → 有环

不会死循环。 队列为空自动退出,不会无限跑。


模板代码

from collections import deque

n, m = map(int, input().split())

g = [[] for _ in range(n + 1)]
indeg = [0] * (n + 1)               # 入度数组

for _ in range(m):
    u, v = map(int, input().split())  # u → v
    g[u].append(v)
    indeg[v] += 1

# 入度为0的点入队
q = deque([i for i in range(1, n + 1) if indeg[i] == 0])
order = []

while q:
    u = q.popleft()                # 弹出队首
    order.append(u)                # 加入结果
    for v in g[u]:                 # 删除出边
        indeg[v] -= 1
        if indeg[v] == 0:          # 入度归零则入队
            q.append(v)

# 判断
if len(order) < n:
    print("有环,无法拓扑排序")
else:
    print(*order)

关键要点

要点 说明
入度 一个点被几条边指着
入队条件 入度为 0
删边 遍历 u 的邻居,入度减 1
环检测 len(order) < n 说明有环
多种答案 入度为 0 的点顺序任意,全正确
复杂度 O(n + m)

posted on 2026-05-12 18:32  ruye07  阅读(18)  评论(0)    收藏  举报