输入样例:
3 3
1 2
2 3
1 3
输出样例:
1 2 3
算法思路:
求拓扑序列是图宽度优先搜索的经典应用,拓扑序列通俗的讲就是所有的边都是从前指向后的,不存在后指向前的,也就保证了图中不存在环。所以有向无环图一定存在拓扑序列,也称为拓扑图。因为边都是从前指向后的,所以图中入度为0的点一定都可以作为起点。
具体算法为:先将图中所有入度为0的结点入队,当队列不空时,出队,并将该结点的邻接点的入度减一,当减一后变为0时,就继续入队,直到队列为空。当最终进入队列的结点数与图中所有的结点数相等时,说明图中无环,并且入队的序列就是拓扑序列。
Java代码:
import java.io.*;
import java.util.*;
public class Main {
static int n, m;
static int []e;
static int []ne;
static int []h;
static int idx;
static int []d; // 存储图中每个结点的入度
static int []seq; // 存储拓扑序列
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String[] split = br.readLine().split(" ");
n = Integer.parseInt(split[0]);
m = Integer.parseInt(split[1]);
e = new int[m];
ne = new int[m];
h = new int[n + 1];
d = new int[n + 1];
seq = new int[n + 1];
Arrays.fill(h, -1);
for(int i = 0; i < m; i++) {
split = br.readLine().split(" ");
int a = Integer.parseInt(split[0]);
int b = Integer.parseInt(split[1]);
add(a, b);
d[b]++; // 处理入度
}
if(topsort()) {
for(int i = 1; i <= n; i++) System.out.print(seq[i] + " ");
}else System.out.println("-1");
}
public static boolean topsort() {
Queue<Integer> qu = new LinkedList<>();
for(int i = 1; i <= n; i++) // 先将初始时的入度入度为0的结点入队
if(d[i] == 0) qu.add(i);
int cnt = 1; // 对拓扑序列计数
while(!qu.isEmpty()) {
int u = qu.poll();
seq[cnt++] = u; // 将出队的结点存储
for(int i = h[u]; i != -1; i = ne[i]) { // 将入读为0的邻接结点的入度减一
int j = e[i];
if(--d[j] == 0) qu.add(j); // 减一后是0的结点继续入队
}
}
return n == cnt - 1; // 当拓扑序列中的结点个数等于图中的结点时,表示存在拓扑序列,图中无环
}
public static void add(int a, int b) {
e[idx] = b;
ne[idx] = h[a];
h[a] = idx++;
}
}