拓扑排序结果长度小于节点总数,说明Kahn算法存在初始化、建图或入队错误;必须用vector indegree(n, 0)和vector graph(n)严格对齐下标,输入边为1-based时立即u--,v--,且需遍历全部0到n−1节点入队indegree[i]==0者,包括孤立点。

拓扑排序结果长度小于节点总数,基本可以断定是 Kahn 算法在入度初始化、边方向或入队时机上出错了——不是算法逻辑错,而是三处关键操作没对齐。
入度数组和邻接表怎么初始化才不越界不漏点
节点编号必须和 vector 下标严格对齐。假设总节点数为 n,就老实用 vector<int> indegree(n, 0)</int> 和 vector<vector>> graph(n)</vector>。
- 别用裸数组
int indegree[1000]而不memset,残留值会导致误判入度为 0 - 输入边是 1-based(如
u=1, v=2)?读入后立刻执行u--, v--,否则所有下标访问都越界 - 孤立节点(无入边也无出边)也要被覆盖:它满足
indegree[i] == 0,必须进初始队列;漏掉任意一个i,结果长度一定 - 若节点编号稀疏(如最大 ID 是 1e9,但实际只有 100 个点),改用
unordered_map<int int> indegree</int>并维护有效节点集合unordered_set<int> nodes</int>
建图时 indegree[v]++ 和 graph[u].push_back(v) 必须同步且方向一致
每条有向边 u → v 只做两件事:更新下游入度、记录出边。反向写就是彻底失效。
- 错误写法:
graph[v].push_back(u)或indegree[u]++—— 这会让整个 BFS 沿着反方向跑 - 重边(两条相同的
u→v)会让indegree[v]被加两次,单次--indegree[v]不足以清零;建图前需用set<pair>></pair>去重 - 不要靠
graph大小推断节点数——graph.size()可能为 0,但节点依然存在;n必须由输入明确给出
BFS 中必须 --indegree[v] 后立即判断是否为 0
这是 Kahn 算法能逐层推进的核心机制。延迟判断或合并检查,会导致节点永久卡在非零入度状态。
立即学习“C++免费学习笔记(深入)”;
- 正确写法:
if (--indegree[v] == 0) q.push(v);—— 减和判是原子操作 - 错误写法:
indegree[v]--; if (indegree[v] == 0) q.push(v);—— 多一次内存访问,调试时容易删掉判断分支 - 别加额外条件,比如
&& !visited[v];indegree[v] == 0本身就是唯一入队依据 - 用
std::queue<int></int>,别用stack或priority_queue(除非真需要字典序最小);FIFO 是保证“谁先没依赖谁先排”的语义基础
怎么可靠检测图中存在环
只看 result.size() != n 就够了。这不是经验判断,是 Kahn 算法的数学结论:环内节点的入度永远无法减到 0,所以它们根本不会进队。
- 队列提前空了但
result没满?直接返回空vector,别试图补救 - 不要检查
indegree数组是否全为 0——未访问节点的值可能是初始 0 或未定义,不能作为依据 - 如果业务要求区分“不连通但无环”和“有环”,得额外跑一遍 DFS 或并查集;Kahn 本身不提供连通性信息
- 真正容易被忽略的是:哪怕只有一个节点被环锁住,整个拓扑序就作废;不存在“部分有效”的拓扑排序


















