图解题库 / 拓扑排序

210. 课程表 II

中等 含交互动画

学完这道你应该能: 说清它为什么用「拓扑排序」,而不是死记步骤; 合上页面,自己默写出核心代码; 用 30 秒把思路讲清楚。

题目描述

给定先修关系,返回一个能修完所有课的学习顺序;有环则返回空数组。

课程 = 0,1,2,3
先修 = 0→1, 0→2, 1→3, 2→3
输出 = [0, 1, 2, 3]

思路解析

和课程表一样的 BFS 拓扑:入度 0 的先入队。每次出队一门课就追加到结果数组,并把它指向的课入度减 1、减到 0 再入队。最后结果长度 == 课程数就有效。

建图 + 入度:标好入度:0 没有前置(入度0),1、2 各 1,3 有两个前置(入度2)。先把 0 入队。

学 0 · 记入顺序:0 出队、加入结果 order=[0]。它指向的 1、2 入度各减 1 变 0,双双入队。

学 1:1 出队、order=[0,1]。它指向 3,把 3 的入度从 2 减到 1(还没到 0,不入队)。

学 2:2 出队、order=[0,1,2]。它也指向 3,把 3 入度减到 0,3 入队。

学 3 · 完成:3 出队、order=[0,1,2,3]。结果长度 4 = 课程数,返回这个顺序 [0,1,2,3]

凡是「给依赖关系排出一个可行执行顺序」都是拓扑排序:课程安排、任务调度、编译依赖、Excel 公式求值。

参考代码(Python)

Python
1indeg=[0]*n; g=defaultdict(list); order=[]
2for a,b in prerequisites: g[b].append(a); indeg[a]+=1   # b→a
3q=deque(i for i in range(n) if indeg[i]==0)
4while q:
5    cur=q.popleft(); order.append(cur)        # 记录顺序
6    for nxt in g[cur]:
7        indeg[nxt]-=1
8        if indeg[nxt]==0: q.append(nxt)
9return order if len(order)==n else []        # 有环则为空

复杂度分析

  • 时间复杂度:O(V+E) —— 每节点和边各处理一次
  • 空间复杂度:O(V+E) —— 邻接表 + 入度 + 队列

套路模板

记住骨架:入度0入队、出队即收顺序、邻居减度归零再入队、长度不足=有环。课程表(判环)就是它去掉 order。

Python
1q=deque(入度0的点); order=[]
2while q:
3    u=q.popleft(); order.append(u)        # 收集顺序
4    for v in g[u]:
5        indeg[v]-=1
6        if indeg[v]==0: q.append(v)
7return order if len(order)==n else []   # 不全=有环

易错点

  • 不判长度直接返回 order → ✅ 长度 != n 时返回空(有环时环上节点进不了 order)
  • 建图方向反 → ✅ [a,b] 先学 b → 边 b→a(方向错则入度全错)

以上文字与上方动画完全同一思路:先看动画建立直觉 → 读文字巩固 → 关掉页面自己默写一遍代码,能写出来才算真的掌握

下一题 →125. 验证回文串 ← 返回题库