转载自:https://www.cnblogs.com/MrSaver/p/9994720.html
认真阅读你就会发现拓扑排序是基于DFS的,只是加入了一个栈来保存结果。
我们首先知道拓扑排序结果是一个线性排列,这说明了一定存在两类点,一类是入度为0,一类是出度为0。(入度为0指的是只想它的边为0,出度指的是它不指向任何边)。
下图演示了一个从0度点出发的一个DFS树:
首先2节点的邻接顶点是1和3,由于我们是DFS,它就会一条路走下去,所以先走左边,即到达1号节点,然后1号节点的邻接顶点是4,所以接下来箭头指向4,4是一个出度为0的节点,它没有邻接顶点,所以不用再往下递归,把4直接保存到栈中。接着返回到1节点,把1压入栈中,然后返回到2节点,接着走右边这条路,到达3号节点,接着从3号节点的邻接顶点出发,但是都已经访问过了,所以返回3后,直接把3压入栈中,最后返回2,把2压入栈中。
所以最后的结果就是2,3,1,4。
最后我们思考一下这个思想的正确性?我们可以这样想,无论我们从哪个节点出发,只要他指向其他顶点,我们就需要先去处理那些顶点,所以这个节点一定是后于其他结点入栈(也就在拓扑排序中先于其他结点),那些DFS深入到最后的节点,已经不指向任何顶点,直接入栈(在拓扑序列中是最后的)。其实我们理解到这里就可以了。
这个思路有点问题,当入度为0的结点不止一个的时候会出错。下面介绍一种传统的方法.
附上链式前向星实现的代码:
#include <iostream>
#include<cstdio>
#include<bits/stdc++.h>
using namespace std;
const int maxn = 100;
int cnt = 0;
int head[maxn];
int vis[maxn];
struct node{
int next;
int to;
}edge[maxn];
void add(int u, int v)
{
edge[cnt].to = v;
edge[cnt].next = head[u];
head[u] = cnt++;
}
stack<int>st;
void topoSort(int s)
{
for(int i = head[s]; ~i; i = edge[i].next){
int to = edge[i].to;
if(!vis[to])
topoSort(to);
}
st.push(s);
vis[s] = 1;
}
int main()
{
int n, m; //n个点m条边
scanf("%d%d", &n, &m);
fill(head + 1, head + 1 + n, -1);
fill(vis + 1, vis + 1 + n, 0);
for(int i = 1; i <= m; i++){
int u, v;
scanf("%d%d", &u, &v);
vis[v] = 1;//标记当前点入度不为0
add(u, v);
}
int s = -1;
//找入度为0的点
for(int i = 1; i <= n; i++){
if(!vis[i]){
s = i;
}
}
//注意重新初始化
fill(vis + 1, vis + n + 1, 0);//标记当前点没被访问
topoSort(s);
while(!st.empty()){
printf("%d", st.top());
st.pop();
if(st.size() != 0)
printf("->");
}
return 0;
}
拓扑排序的常规方法就是:1.找到入度为0的结点,2.删除这个结点,删除与这个结点相连的边,3.重复1,2直到所有点入度都变成0
以下是链式前向星实现的代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 10;
int head[maxn];
int ingreee[maxn];
int vis[maxn];
int cnt = 0;
struct node{
int to;
int next;
// int vis;
}edge[maxn];
int n, m;
void add(int u, int v)
{
edge[cnt].to = v;
edge[cnt].next = head[u];
head[u] = cnt++;
// edge[cnt].vis = 0;//这条边还没被删除
}
queue<int>q;
void topoSort()
{
int cnt = 0;
while(cnt < n){
int s = -1;
for(int i = 1; i <= n; i++){
if(ingreee[i] == 0 && vis[i] == 0){//找入度为0的点
s = i;
break;
}
}
for(int i = head[s]; ~i; i = edge[i].next){
int to = edge[i].to;
// if(!edge[i].vis){//如果当前边没被删除,删除它,入度减1
ingreee[to]--;
// edge[i].vis = 1;
// }
}
q.push(s);
vis[s] = 1;
cnt++;
}
}
void slove()
{
// scanf("%d%d", &n, &m);
fill(head + 1, head + n + 1, -1);
fill(ingreee + 1, ingreee + n + 1, 0);
fill(vis + 1, vis + n + 1, 0);
for(int i = 1; i <= m; i++){
int u, v;
scanf("%d%d", &u, &v);
add(u, v);
ingreee[v]++;
}
topoSort();
while(!q.empty()){
printf("%d", q.front());
q.pop();
if(q.size()>0)printf("->");
}
printf("\n");
}
int main()
{
while(~scanf("%d%d", &n, &m)){
slove();
}
return 0;
}