给定一个 n 个点 m 条边的有向图,点的编号是 1 到 n,图中可能存在重边和自环。
请输出任意一个该有向图的拓扑序列,如果拓扑序列不存在,则输出 −1。
若一个由图中所有点构成的序列 A 满足:对于图中的每条边 (x,y),x 在 A 中都出现在 y 之前,则称 A 是该图的一个拓扑序列。
输入格式
第一行包含两个整数 n 和 m。
接下来 m 行,每行包含两个整数 x 和 y,表示存在一条从点 x 到点 y 的有向边 (x,y)
输出格式
共一行,如果存在拓扑序列,则输出任意一个合法的拓扑序列即可。
否则输出 −1。
数据范围
1≤n,m≤10^5
输入样例:
3 3
1 2
2 3
1 3
输出样例:
1 2 3
有向无环图可以得到拓扑排序,拓扑排序就是只有从前指向后的边,比如图里面有一条从a指向b的边,那么在拓扑序列里面,a一定在b的前面。可以参考这篇文章:拓扑排序 (算法思想+图解+模板+练习题)_拓扑排序模板-CSDN博客
我们求一个拓扑序列,可以使用队列的方式,先找到图里面入度为0的点作为序列的起点,再将这些点删除,然后找到他们指向的点,此时这些指向的点存在入度为0的情况,再次入队,以此类推,知道图里所有点都入队为止。
bool topsort()
{
int hh=0,tt=-1; //队头队尾
for(int i=1;i<=n;i++) //点的编号是从1开始的,从头开始找入度为0的点
{
if(in[i]==0) //这个点的入度为0就把它入队(队尾插入一个数,第一次插就是q[0],既是队头又是队尾)
{
q[++tt]=i;
}
}
while(hh<=tt) //队列不为空的时候
{
int t=q[hh++]; //取出队头的点,它的入度为0
for(int i=h[t];i!=-1;i=ne[i]) //遍历这个点的所有指向的点
{
int j=e[i];
in[j]--; //将指向的点入度-1
if(in[j]==0) //判断指向的点入度是否为0,如果为0就入队
{
q[++tt]=j;
}
}
}
return tt==n-1; //判断是否所有点都入队,有n个点,队列下标是从0开始的,所以第n个点的下标是n-1
//如果所有的点都入队就说明他是一个有向无环图,是符合拓扑排序的
//这里用数组模拟队列是为了能把这个拓扑序列保存下来,后面输出
}
这里我们用数组模拟队列是因为后面需要输出这个拓扑序列,而队列中入队的元素组合起来就是拓扑序列,如果用STL库里面的queue,删除之后就没法找到这些元素了,用数组模拟队列的删除只是将代表队头和队尾的下标改变了,实际上元素还存在队列里面。
注意:拓扑排序的序列不唯一
示例代码:
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=1e5+10;
int n,m;
int h[N],e[N],ne[N],idx;
int q[N]; //队列,存放拓扑序列
int in[N]; //存放各个点的入度
void add(int a,int b) //从a到b的边
{
e[idx]=b;
ne[idx]=h[a];
h[a]=idx;
idx++;
}
bool topsort()
{
int hh=0,tt=-1; //队头队尾
for(int i=1;i<=n;i++) //点的编号是从1开始的,从头开始找入度为0的点
{
if(in[i]==0) //这个点的入度为0就把它入队(队尾插入一个数,第一次插就是q[0],既是队头又是队尾)
{
q[++tt]=i;
}
}
while(hh<=tt) //队列不为空的时候
{
int t=q[hh++]; //取出队头的点,它的入度为0
for(int i=h[t];i!=-1;i=ne[i]) //遍历这个点的所有指向的点
{
int j=e[i];
in[j]--; //将指向的点入度-1
if(in[j]==0) //判断指向的点入度是否为0,如果为0就入队
{
q[++tt]=j;
}
}
}
return tt==n-1; //判断是否所有点都入队,有n个点,队列下标是从0开始的,所以第n个点的下标是n-1
//如果所有的点都入队就说明他是一个有向无环图,是符合拓扑排序的
//这里用数组模拟队列是为了能把这个拓扑序列保存下来,后面输出
}
int main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>n>>m;
memset(h,-1,sizeof(h)); //领接表初始化
while(m--)
{
int a,b;
cin>>a>>b;
add(a,b); //增加一条从a到b的边
in[b]++;
}
if(!topsort()) //如果拓扑序列不存在就输出-1
{
puts("-1");
}
else
{
for(int i=0;i<n;i++)
{
cout<<q[i]<<" "; //按顺序输出它的拓扑序列
}
cout<<endl;
}
return 0;
}