拓扑序列定义
若一个由图中所有点构成的序列A满足:对于图中的每条边(x -> y),x在A中都出现在y之前,则A是一个拓扑序列。
- 拓扑序列只存在于有向图中。
- 可以证明一个有向无环图一定存在拓扑序列。
- 如果图中有环,该图一定没有拓扑序列。
如何求拓扑序列:
一个有向图,如果图中有入度为 0 的点,就把这个点删掉,同时也删掉这个点所连的边。
一直进行上面的处理,如果所有点都能被删掉,则这个图可以进行拓扑排序。
入度:别的点指向这个点的边数。
入度为0则可以认为该点在当前图的其他入度不为0的点的前面。
解题思路
首先记录各个点的入度
然后将入度为 0 的点放入队列
将队列里的点依次出队列,然后找出所有出队列这个点发出的边,删除边,同事边的另一侧的点的入度 -1。
如果所有点都进过队列,则可以拓扑排序,输出所有顶点。否则输出-1,代表不可以进行拓扑排序。
给定一个 n 个点 m 条边的有向图,点的编号是 1 到 n,图中可能存在重边和自环。
请输出任意一个该有向图的拓扑序列,如果拓扑序列不存在,则输出 −1。
若一个由图中所有点构成的序列 A 满足:对于图中的每条边 (x,y),x 在 A 中都出现在 y 之前,则称 A 是该图的一个拓扑序列。
输入格式
第一行包含两个整数 n 和 m。
接下来 m 行,每行包含两个整数 x 和 y,表示存在一条从点 x 到点 y 的有向边 (x,y)。
输出格式
共一行,如果存在拓扑序列,则输出任意一个合法的拓扑序列即可。
否则输出 −1−1。
数据范围
1≤n,m≤10^5
输入样例:
3 3 1 2 2 3 1 3
输出样例:
1 2 3
import java.io.*;
import java.util.*;
class Main{
static int N = 100010;
static int n,m,idx,k;
static int[] h = new int[N];
static int[] e = new int[2*N];
static int[] ne = new int[2*N];
static int[] enter = new int[N];// 计算入度
static int[] p = new int[N]; // 存储排序后的序列
static Queue<Integer> q = new LinkedList<>();
public static void main(String[] args) throws IOException{
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
String[] s = in.readLine().split(" ");
n = Integer.parseInt(s[0]);
m = Integer.parseInt(s[1]);
for(int i=1;i<=n;i++) // 初始化h[]
h[i] = -1;
while(m-->0){
s = in.readLine().split(" ");
int a = Integer.parseInt(s[0]);
int b = Integer.parseInt(s[1]);
add(a,b); // 添加边
}
for(int i=1;i<=n;i++){ // 初始将所有入度为0的点都装入队列中
if(enter[i]==0)
q.add(i);
}
bfs();
if(k==n){ // k==n表示所有的点入度都为0,没有环
for(int i=0;i<n;i++)
System.out.print(p[i]+" ");
}
else System.out.println("-1");
}
public static void bfs(){
while(!q.isEmpty()){
int t = q.poll();
p[k++] = t;
for(int i=h[t];i!=-1;i=ne[i]){
int j = e[i];
enter[j]--;
if(enter[j]==0)
q.add(j);
}
}
}
public static void add(int a, int b){
e[idx] = b;
ne[idx] = h[a];
h[a] = idx++;
enter[b]++;
}
}