# 拓扑排序
只能应用于有向图
- 一个有向图,如果图中有入度为 0 的点(必要条件),就把这个点删掉,同时也删掉这个点所连的边。
- 一直进行上面出处理,如果所有点都能被删掉,则这个图可以进行拓扑排序。 不能够进行拓扑排序的图的充分条件:
- 存在环
可以证明 有向无环图 一定能够进行 拓扑排序,有向无环图也被称为 拓扑图
一个 拓扑图 至少存在一个 入度为 0 的点
# 拓扑序列
满足拓扑排序的一个序列
# 应用场景
- 拓扑排序的重要应用:判断 AOV 网中是否存在环。判定方法:对有向图构造拓扑排序,如果所有顶点都在它的拓扑序列中,则该 AOV 网必定不存在环。
# 算法过程
- 在图中找出所有入度为 0 的点,加入队列 queue
- 枚举 队列中的 所有点 :顶点 t = queue.head() ==> 拆点操作,拆掉这个点和所有相连的边
- 枚举 顶点 t 的所有出边: t -> j
- 删掉 t->
- 重复上面的操作,直到所有点都已拆除;
- 最后,得到的拓扑序列中已经包含了所有点(除了环中的所有点,因为环中的所有点 入度都不为 0 ) 拓扑排序实际上是 的一种特殊情况,每次加入队列的点是入度为 0 的点,加入队列后拆掉相邻的边;记录已经拆掉的点
# 代码实现
AcWing 848. 有向图的拓扑序列 (opens new window)
#include <iostream>
#include<cstring>
using namespace std;
const int N = 1e5+10;
int h[N], e[N], ne[N], idx = 0; // 初始化图
int d[N], q[N]; // d[N] 入度数组 ; q[N]: bfs队列
int n, m;
void add(int a, int b){ // 加边
e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
bool topsort(){ // 拓扑排序;返回是否是拓扑图
int hh = 0, tt = -1;
for (int i = 1; i< n; i++){ // 入度为 0 的顶点 入队
if (!d[i]) 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);
for (int i =0; i< m; i++){
int a,b;
cin >> a>> b;
add(a, b); // 图 加 边
d[b]++; // 入度 +1
}
if (topsort()){
for (int i =0; i< n; i++){ // 队列中的顺序就是 一个合法的拓扑序列
cout << q[i] << " ";
}
cout << endl;
}else {
cout << -1 << endl;
}
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
← 0x24_广度优先搜索 0x30_数学 →