什么是拓扑排序
对一个有向无环图G进行拓扑排序,是将G中所有顶点排成一个线性序列,使得图中任意一对顶点u和v,若边<u,v>∈E(G),则u在线性序列中出现在v之前。通常,这样的线性序列称为满足拓扑次序的序列,简称拓扑序列。简单的说,由某个集合上的一个偏序得到该集合上的一个全序,这个操作称之为拓扑排序。
摘自百度百科
用通俗易懂的语言讲,就是对一个 有向无环图 通过一系列操作得到一个序列,该序列中的每一个元素都没一条指向他前面的元素的边。
怎么进行拓扑排序
上文说到要对一个 DAG 进行一系列操作,那么这一系列操作是什么呢。
从拓扑序的性质来看,DAG 中的每一条边都是从前面的元素指向后面的元素,那么最前面的元素显然是没有入度的,那么首先我们在建图的时候,要统计每个点的入度,建完图我们可以扫一遍每个元素,将每一个入度为 0 0 0 的点压入队列。
将全部的入度为 0 0 0 的点压入队列后,我们可以依次将这些点从图中删去。每删去一个点,把以他为顶点的边也删去,把这条边连着的另一顶点的入度减去 1 1 1 ,如果这个点的入度也为 0 0 0,就将他压入队列,知道队列里的元素为空。每次从队列里取出要删的点时,用一个数组将其记录,最后就可以得到这个图的拓扑序了。
代码实现:
#include<bits/stdc++.h>
#define reg register
#define ri reg int
using namespace std;
const int Maxn=5*1e5+5;
int n,m;
int tot,head[Maxn];
int In[Maxn];
int Ans[Maxn],cnt;
struct node{
int v,nxt;
}edge[Maxn];
inline void Add_edge(int u,int v) {
edge[++tot].v=v;
edge[tot].nxt=head[u];
head[u]=tot;
}
queue<int> q;
int main(){
scanf("%d%d",&n,&m);
for(ri i(1),u,v;i<=m;++i) {
scanf("%d%d",&u,&v);
Add_edge(u,v);
++In[v];
}
for(ri i(1);i<=n;++i)
if(In[i]==0)
q.push(i);
while(!q.empty()) {
ri now=q.front();
q.pop();
Ans[++cnt]=now;
for(ri i(head[now]) ;i ;i=edge[i].nxt) {
ri v=edge[i].v;
--In[v];
if(In[v]==0) q.push(v);
}
}
for(ri i=1;i<=cnt;++i) printf("%d ",Ans[i]);
return 0;
}
注意
只有有向无环图才能拓扑排序,每张图的拓扑序不唯一。