拓扑排序算法
808 字
4 分钟
拓扑排序算法

拓扑排序算法
拓扑排序用于有向无环图,也就是 DAG。
它要解决的问题是:如果一些任务之间有先后依赖,能不能排出一个合法顺序。
比如:
先学 A,才能学 B先学 B,才能学 C那么一种合法顺序是:
A B C1、什么是拓扑序
在有向图中,如果有一条边:
a -> b表示 a 必须排在 b 前面。
如果一个序列满足所有边的先后关系,它就是拓扑序。
只有有向无环图才存在拓扑序。如果图里有环,就无法排出合法顺序。
2、核心思想:入度为 0 的点先处理
入度表示有多少条边指向这个点。
如果一个点入度为 0,说明没有任何前置条件,可以先做。
拓扑排序的过程:
1. 把所有入度为 0 的点入队2. 每次取出队头点 t3. 删除 t 指向其他点的边4. 如果某个点入度变成 0,就入队5. 最后看是否处理了所有点3、为什么能判断环
如果图里有环,环上的每个点都至少有一个前置点也在环里。
这些点的入度不会变成 0,所以最后无法全部入队。
判断方式:
return tt == n - 1;队列里存下来的节点数量等于 n,说明所有点都被处理了。
4、C++ 模板
#include <bits/stdc++.h>using namespace std;
const int N = 100010;const int M = 200010;
int n, m;int h[N], e[M], ne[M], idx;int d[N];int q[N];
void add(int a, int b) { e[idx] = b; ne[idx] = h[a]; h[a] = idx++;}
bool topsort() { int hh = 0; int tt = -1;
for (int i = 1; i <= n; i++) { if (d[i] == 0) q[++tt] = i; }
while (hh <= tt) { int t = q[hh++];
for (int i = h[t]; i != -1; i = ne[i]) { int j = e[i]; d[j]--;
if (d[j] == 0) { q[++tt] = j; } } }
return tt == n - 1;}
int main() { cin >> n >> m; memset(h, -1, sizeof h);
while (m--) { int a, b; cin >> a >> b; add(a, b); d[b]++; }
if (topsort()) { for (int i = 0; i < n; i++) { cout << q[i] << " "; } cout << endl; } else { cout << "存在环,无拓扑序列" << endl; }
return 0;}5、手推一个例子
有边:
1 -> 21 -> 32 -> 43 -> 4一开始:
入度为 0 的点:1处理 1 后,2 和 3 入度变成 0,入队。
处理 2 后,4 入度从 2 变成 1。
处理 3 后,4 入度从 1 变成 0,入队。
最后得到一种拓扑序:
1 2 3 4也可能是:
1 3 2 4拓扑序不一定唯一。
6、容易写错的地方
(1)加边时要维护入度
add(a, b);d[b]++;有向边 a -> b 会让 b 的入度加一。
(2)初始化邻接表
memset(h, -1, sizeof h);链式前向星必须初始化表头。
(3)判断条件是 tt == n - 1
数组队列从下标 0 开始,最后一个元素位置是 n - 1。
如果处理节点数不够,说明有环。
总结
拓扑排序的关键词是:
入度为 0 的点先入队它既能输出一个满足依赖关系的顺序,也能判断有向图中是否存在环。
文章分享
如果这篇文章对你有帮助,欢迎分享给更多人!














