给定一个n个点m条边的有向图,图中可能存在重边和自环。
请输出任意一个该有向图的拓扑序列,如果拓扑序列不存在,则输出-1。
若一个由图中所有点构成的序列A满足:对于图中的每条边(x, y),x在A中都出现在y之前,则称A是该图的一个拓扑序列。
输入格式
第一行包含两个整数n和m
接下来m行,每行包含两个整数x和y,表示存在一条从点x到点y的有向边(x, y)。
输出格式
共一行,如果存在拓扑序列,则输出拓扑序列。
否则输出-1
数据范围
(1≤n,m≤105)
输入样例
3 3
1 2
2 3
1 3
输出样例
1 2 3
一道拓扑排序的模板题。拓扑排序的算法是,首先找到入度为0的点(所以需要一个数组to[]
维护每个结点的入度),将它放入队列,然后依次枚举它的所有出边t->j
,删掉它们之间的连接to[j]--
,如果此时to[j]==0
,那么将j
入队,持续这个过程直到队列为空。
代码:
#include <iostream>
#include <queue>
#include <memory.h>
using namespace std;
const int N = 1e5+10;
int h[N], e[N], ne[N], idx;
int to[N], ans[N];
int n, m;
void add(int a, int b){
e[idx] = b; ne[idx] = h[a]; h[a] = idx++; //邻接表加入边的一般操作
}
bool toposort(){
queue<int> q;
int pp = 0;
for(int i = 1; i <= n; ++i){ //首先找到入度为0的结点,将它加入队列和答案中
if(to[i] == 0){
q.push(i);
ans[pp++] = i;
break;
}
}
while(!q.empty()){
int u = q.front(); q.pop(); //取出队首元素
for(int i = h[u]; i != -1; i = ne[i]){ //对每个u的出边
int v = e[i]; to[v]--; //删掉u->v的连接
if(to[v] == 0) { //如果v的入度为0了,那么加入队列和答案中
ans[pp++] = v;
q.push(v);
}
}
}
return pp==n;
}
int main(){
scanf("%d%d", &n, &m);
memset(h, -1, sizeof(h));
for(int i = 0; i < m; ++i){
int a, b;
scanf("%d%d", &a, &b);
add(a, b); //将a->b的边加入
to[b]++; //更新入度信息
}
if(toposort()){ //如果有拓扑序,打印出结果
for(int i = 0; i < n; ++i) printf("%d ", ans[i]);
}else
printf("-1");
}