拓扑排序仅适用于有向无环图(dag),kahn算法通过入度为0的节点入队实现bfs式遍历,需严格校验输出长度与节点总数是否一致以判断环;必须用std::queue而非std::stack,确保顺序稳定;indegree与adj索引须对齐节点id,初始化与重置不可遗漏。

拓扑排序前必须检查图是否有环
拓扑排序只对有向无环图(DAG)有效,Kahn 算法本身不主动报错,但若图含环,它会提前终止且输出节点数少于总节点数——这是唯一可靠信号。indegree 数组初始化后,需遍历所有节点确认入度为 0 的节点是否至少有一个;若没有,图必然有环。别依赖算法返回“成功”标志,要自己比对结果长度和节点总数。
用 std::queue 而不是 std::stack 实现 BFS 式遍历
Kahn 算法本质是广度优先的入度驱动过程:每次取一个入度为 0 的节点,删掉它的出边,更新邻居入度。用 std::queue 符合 FIFO 逻辑,保证更早入队的节点优先处理,结果稳定可复现;若误用 std::stack,会变成 DFS 风格,虽仍可能得到合法拓扑序,但顺序不可控,且容易在调试时误导判断。
常见错误:把邻接表遍历写成 for (auto& v : adj[u]) 却忘了在更新 indegree[v] 后加判断:
if (--indegree[v] == 0) {
q.push(v);
}
adj 和 indegree 的索引必须严格对齐节点 ID
节点编号若从 1 开始(比如输入给的是 1~n),indegree 数组就得开 n+1 大小,下标 0 闲置;若节点是字符串或自定义结构体,不能直接用数组,得用 std::unordered_map<nodeid int></nodeid> 存 indegree,同时 adj 也得用同类型 key 的 map 或 vector
典型坑点:
- 读入边
u -> v后只做了adj[u].push_back(v),却忘了indegree[v]++ - 节点数 n 已知,但循环只跑了
i = 0到n-1,而实际节点编号是 1~n,导致indegree[0]永远为 0 干扰队列初始状态
多解时 Kahn 算法输出取决于入度为 0 的节点插入队列的顺序
当多个节点同时入度为 0,谁先入队谁先出队,直接影响最终序列。标准实现用 std::queue,按输入顺序或遍历顺序插入,结果确定但非唯一;若需要字典序最小的拓扑序,得改用 std::priority_queue<int vector>, greater<int>></int></int>,每次取最小编号节点。
注意:std::priority_queue 会带来 O(log n) 插入开销,对稀疏图影响不大,但节点数超 10⁵ 时需权衡;另外,必须确保所有入度为 0 的节点都已加入堆,不能边处理边漏加。
真正容易被忽略的是:即使图无环,若未显式清空队列或重置 indegree 数组,在多次调用同一函数时会复用旧状态,导致结果错乱——每次运行前务必重新初始化。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











