拓扑排序
从离散数学的角度定义,假设(A,≤)是有限偏序集,对其进行拓扑排序是指将其扩展成一个全序集,使得≤∈<,即对任意的a,b∈A,若a≤b,则a<b。
从图论的角度定义,对一个有向无环图G进行拓扑排序,是将G中所有的顶点排成一个线性序列,使得图中任意一对顶点u和v,如果(u,v)∈E(G),则u在线性序列中应出现在v之前。
思路
从离散数学的角度定义,拓扑排序是针对有限偏序集的,由离散数学的知识知,若(A,≤)是偏序集,则拟序集(A,<)中不存在长度大于1的环(这其实与无环图对应),所以有限偏序集可以画成哈斯(Hasse)图。Hasse图非空时总存在极小元,每次输出极小元,更新Hasse图,直到Hasse为空。
从图论的角度,极小元等同于图中入度为0的顶点,每次输出入度为0的点,更新与之相连点的入度,直到输出全部顶点。如果存在顶点没有输出,说明存在环。
样题
假设有n个任务,还有m个二元组(u,v),(u,v)表示u必须在v之前执行。请输出n个任务按顺序执行的一种可能情况。
代码实现
邻接矩阵+队列
1 #include<stdio.h> 2 #include<cstring> 3 #include<queue> 4 #include<vector> 5 using namespace std; 6 7 const int V = 100 + 10; //最大顶点数 8 const int E = 100 * 100 + 10; //最大边数 9 vector<int>e[V]; //邻接矩阵存图 10 int in[V]; //顶点的入度 11 12 int m, n; 13 14 int main() 15 { 16 while (scanf("%d%d", &n, &m) == 2 && n) 17 { 18 for (int i = 0; i < n; i++) 19 { 20 e[i].clear(); 21 in[i] = 0; 22 } 23 for (int i = 0; i < m; i++) 24 { 25 int from, to; 26 scanf("%d%d", &from, &to); 27 e[from].push_back(to); 28 in[to]++; 29 } 30 vector<int>ans; 31 queue<int>q; 32 for (int i = 1; i <= n; i++) if (!in[i]) q.push(i); //将入度为0的入队 33 while (!q.empty()) 34 { 35 int u = q.front(); q.pop(); 36 ans.push_back(u); 37 for (int i = 0; i < e[u].size(); i++) //遍历与之相连的顶点 38 if ((--in[e[u][i]]) == 0) q.push(e[u][i]); //入度减1,如果为0,入队 39 } 40 for (int i = 0; i < ans.size(); i++) 41 printf("%d ", ans[i]); 42 printf(" "); 43 } 44 return 0; 45 }
dfs
1 #include<stdio.h> 2 #include<iostream> 3 #include<cstring> 4 #include<algorithm> 5 #include<vector> 6 #include<cstring> 7 using namespace std; 8 9 const int maxn = 100 + 10; 10 vector<int>G[maxn]; 11 int c[maxn], topo[maxn], t; 12 int n, m; 13 14 bool dfs(int u) 15 { 16 c[u] = -1; //正在访问,dfs(u)正在栈帧中,尚未返回 17 for (int i = 0; i < G[u].size(); i++) 18 { 19 if (G[u][i]) 20 { 21 if (c[G[u][i]] < 0) return false; 22 else if (!c[G[u][i]] && !dfs(G[u][i])) return false; 23 } 24 } 25 c[u] = 1; //已经访问过,dfs(u)已被调用过,并已访问 26 topo[--t] = u; 27 return true; 28 } 29 30 bool toposort() 31 { 32 t = n; 33 memset(c, 0, sizeof(c)); 34 for (int i = 1; i <= n; i++) 35 if (!c[i] && !dfs(i)) //c[i]为0表示从未访问过 36 return false; 37 return true; 38 } 39 40 void slove() 41 { 42 if (toposort()) 43 { 44 for (int i = n - 1; i >= 0; i--) 45 printf("%d ", topo[i]); 46 printf(" "); 47 } 48 } 49 50 int main() 51 { 52 int u, v; 53 while (scanf("%d%d", &n, &m) == 2 && n) 54 { 55 for (int i = 0; i <= n; i++) G[i].clear(); 56 for (int i = 0; i < m; i++) 57 { 58 scanf("%d%d", &u, &v); 59 G[v].push_back(u); 60 } 61 slove(); 62 } 63 return 0; 64 }
参考链接:
https://baike.baidu.com/item/拓扑排序/5223807?fr=aladdin