问题背景
给定一个 n 个点 m 条边的有向图,点的编号是 1 到 n,图中可能存在重边和自环。
请输出任意一个该有向图的拓扑序列,如果拓扑序列不存在,则输出 −1。
若一个由图中所有点构成的序列 A 满足:对于图中的每条边 (x,y),x 在 A 中都出现在 y 之前,则称 A 是该图的一个拓扑序列。
思路
几个性质:
- DAG (有向无环图)必定有一个拓扑序列
- 拓扑序列不唯一
算法步骤:
- 用d数组记录每个点的入度,如果有某个点的入度是0,那么可以取出放在答案序列res中
- 首先是遍历操作,把每个入度为0点的点加入到队列q中
- 当队列不空的时候,持续以下操作:把队头加入到答案序列,弹出队头,遍历队头连接的所有点,将该点的入度-1(表示删除这条边),如果该点的入度为0,那么加入到队列q中
代码
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
const int N=1e5+10;
queue<int> q;
vector<int> v[N];
vector<int> res;
int d[N];//储存每个点的入度
int n,m;
int topo()
{
for(int i=1;i<=n;i++) //遍历n个点
if(d[i]==0) q.push(i);//把入度为0的节点入队
while(!q.empty())//宽度优先搜索
{
int t=q.front();
q.pop();
res.push_back(t);
for(auto x:v[t])//x是与t连接的点,依次遍历
{
d[x]--;//去掉t->x的边,因此x的入度-1
if(d[x]==0) q.push(x);
}
}
if(res.size()==n) return 1;
else return 0;
}
int main()
{
cin>>n>>m;
//vector模拟邻接表读入图的数据
while(m--)
{
int x,y;
cin>>x>>y;
v[x].push_back(y);
d[y]++;//y点入度+1
}
if(topo())
for(int i=0;i<res.size();i++) cout<<res[i]<<' ';
else
puts("-1");
return 0;
}