本文最后更新于:2022年8月25日 下午
                  
                
              
            
            
              
                
                UPD 2022/7/4:删除了一大堆没有意义的内容。
Definition
拓扑排序是什么?
就是在一个 DAG 上边,将所有的点排成一个线性的序列,使得每个点的起点都在终点前边。
那它可以用来干什么呢?
这可以用来处理一些层次关系,就是如果你想做事件 $A$,必先处理完事件 $B$,类似这样的关系。
实现
我们用 BFS 来处理拓扑排序。
我们拿这张图举例:

根据所有的起点都要在终点前边这句话,我们首先要找到入度为 0 的点进行我们的拓扑排序。
这张图的拓扑序也就是:
其中,2 和 3 的位置可以调换。
那么,我们就可以给这个图来个 BFS,每次看看这个点入度是否为 0,然后先把入度为 0 的点扔进队列。接下来输出这个点,然后开始遍历这个点所到达的每一个点,将到达的点的入度减一(即:删去这条边),然后看一看到达的点的入度是否为 0,如果为 0 就压进队列,然后重复上面的过程直到队列为空。
这边强烈建议看着思路写出来,不要看接下来的代码,因为这就是最简单的拓扑排序的模板,写出来是很简单的。
| 12
 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
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 
 | #include <cstdio>#include <algorithm>
 #include <queue>
 #define N 100005
 using namespace std;
 
 queue <int> q;
 
 int n, m, in[N];
 
 struct Graph {
 int y, nxt;
 }e[N];
 
 int ltp, lk[N];
 
 int read() {
 int x = 0, w = 1;
 char c = getchar();
 
 while(c < '0' || c > '9') {
 if(c == '-') {
 w = -1;
 }
 c = getchar();
 }
 while(c >= '0' && c <= '9') {
 x = x*10+(c-'0');
 c = getchar();
 }
 return x * w;
 }
 
 void write(int x) {
 if(x < 0) {
 x = -x;
 putchar('-');
 }
 if(x > 9) {
 write(x / 10);
 }
 
 putchar(x % 10 + '0');
 }
 
 void insert(int u, int v) {
 e[++ltp] = (Graph){v, lk[u]};
 lk[u] = ltp;
 }
 
 void top_sort() {
 while(!q.empty()) {
 int p = q.front();
 q.pop();
 write(p);
 putchar(' ');
 
 for(int i=lk[p]; i; i=e[i].nxt) {
 int y=e[i].y;
 in[y]--;
 if(!in[y]) {
 q.push(y);
 }
 }
 }
 }
 
 void inp() {
 n = read(), m = read();
 while(m--) {
 int u = read(), v = read();
 insert(u, v);
 in[v]++;
 }
 }
 
 void work() {
 for(int i=1; i<=n; i++) {
 if(!in[i]) {
 q.push(i);
 }
 }
 
 top_sort();
 }
 
 int main() {
 
 
 inp();
 work();
 
 
 return 0;
 }
 
 | 
使用拓扑排序判环
如果给你的图不是个 DAG,那怎么办呢?
既然他有环,我们就得看看它是不是存在环,如果存在我们就得反映出来。
来个图:

其实也简单,你会发现 2 3 4 组成的环会把一些点重复弹出队列。
根据这个性质,你每次把一个数弹出队列的时候就把弹出次数自增,最后看看是不是和结点数一样就可以了。
| 12
 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
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
 100
 101
 102
 103
 104
 105
 106
 
 | #include <cstdio>#include <algorithm>
 #include <queue>
 #define N 100005
 using namespace std;
 
 queue <int> q;
 
 int n, m, in[N];
 
 struct Graph {
 int y, nxt;
 }e[N];
 
 int ltp, lk[N];
 int ans[N];
 int cnt, pos;
 
 int read() {
 int x = 0, w = 1;
 char c = getchar();
 
 while(c < '0' || c > '9') {
 if(c == '-') {
 w = -1;
 }
 c = getchar();
 }
 while(c >= '0' && c <= '9') {
 x = x*10+(c-'0');
 c = getchar();
 }
 return x * w;
 }
 
 void write(int x) {
 if(x < 0) {
 x = -x;
 putchar('-');
 }
 if(x > 9) {
 write(x / 10);
 }
 
 putchar(x % 10 + '0');
 }
 
 void insert(int u, int v) {
 e[++ltp] = (Graph){v, lk[u]};
 lk[u] = ltp;
 }
 
 void top_sort() {
 while(!q.empty()) {
 int p = q.front();
 q.pop();
 ans[++pos] = p;
 cnt++;
 
 for(int i=lk[p]; i; i=e[i].nxt) {
 int y=e[i].y;
 in[y]--;
 if(!in[y]) {
 q.push(y);
 }
 }
 }
 }
 
 void inp() {
 n = read(), m = read();
 while(m--) {
 int u = read(), v = read();
 insert(u, v);
 in[v]++;
 }
 }
 
 void work() {
 for(int i=1; i<=n; i++) {
 if(!in[i]) {
 q.push(i);
 }
 }
 
 top_sort();
 
 if(cnt == n) {
 for(int i=1; i<=pos; i++) {
 write(ans[i]);
 putchar(' ');
 }
 } else {
 printf("Leave me alone");
 }
 }
 
 int main() {
 
 
 inp();
 work();
 
 
 return 0;
 }
 
 | 
拓扑排序的时间复杂度是 $O(n +m)$,挺快的。