拓扑排序算法

808 字
4 分钟
拓扑排序算法

拓扑排序算法#

拓扑排序用于有向无环图,也就是 DAG。

它要解决的问题是:如果一些任务之间有先后依赖,能不能排出一个合法顺序。

比如:

先学 A,才能学 B
先学 B,才能学 C

那么一种合法顺序是:

A B C

1、什么是拓扑序#

在有向图中,如果有一条边:

a -> b

表示 a 必须排在 b 前面。

如果一个序列满足所有边的先后关系,它就是拓扑序。

只有有向无环图才存在拓扑序。如果图里有环,就无法排出合法顺序。

2、核心思想:入度为 0 的点先处理#

入度表示有多少条边指向这个点。

如果一个点入度为 0,说明没有任何前置条件,可以先做。

拓扑排序的过程:

1. 把所有入度为 0 的点入队
2. 每次取出队头点 t
3. 删除 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 -> 2
1 -> 3
2 -> 4
3 -> 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 的点先入队

它既能输出一个满足依赖关系的顺序,也能判断有向图中是否存在环。

文章分享

如果这篇文章对你有帮助,欢迎分享给更多人!

拓扑排序算法
https://blog.tjcheng.cn/posts/migrated/old-94/
作者
TengJinCheng
发布于
2026-03-10
许可协议
CC BY-NC-SA 4.0
Profile Image of the Author
TengJinCheng
Hello, I'm Teng.
公告
欢迎来到TengJinCheng工作铺!
音乐
封面

音乐

暂未播放

0:000:00
暂无歌词
分类
标签
站点统计
文章
157
分类
7
标签
114
总字数
109,939
运行时长
0 天
最后活动
0 天前
站点信息
构建平台
Local
博客版本
Firefly v6.13.4
文章许可
CC BY-NC-SA 4.0

文章目录