摘要
本文主要介紹拓撲序列,和求解拓撲序列的方法。
什麼是拓撲序列
拓撲序列是對於有向圖而言的,有向圖的拓撲序是其頂點的線性排序,使得對於從頂點 到頂點的每個有向邊, 在序列中都在之前。
例如對於下圖:
對於上圖, 存在4條邊:(1,3)(1,2)(2,4)(2,3)
該圖的拓撲序必須要滿足以下兩點:
- 每個頂點只出現一次。
- 對於圖中的任何一條邊,起點必須在終點之前。
拓撲序的求法
首先,不是所有的有向圖都是有拓撲序的,只有有向無環圖纔有拓撲序,所以有向無環圖又被稱爲拓撲圖。
拓撲序是按照點的先後順序排列的,也就是說入度爲0的點一定是排在前面的,我們直接對一個圖BFS一遍,BFS過程中更新每個點的入度,如果一個點的入度爲0,那麼就將其加入拓撲序,並且刪除其與後繼結點的所有邊。
在讀入邊的時候,直接計算點的入度。
代碼:
import java.io.*;
import java.util.*;
public class Main{
static BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
static BufferedWriter out = new BufferedWriter(new OutputStreamWriter(System.out));
static final int N = 100010;
static int n, m, idx = 1;
static int e[] = new int[N];
static int ne[] = new int[N];
static int h[] = new int[N];
static int d[] = new int[N];
static Queue<Integer> q = new LinkedList<>();
static Queue<Integer> ans = new LinkedList<>();
public static int Int(String s){return Integer.parseInt(s);}
public static void add(int a, int b){
e[idx] = b;
ne[idx] = h[a];
h[a] = idx++;
}
public static Boolean bfs(){
while(!q.isEmpty()){
int x = q.peek();
q.poll();
for(int i = h[x]; i != 0; i = ne[i]){ // 遍歷所有後繼結點
if(--d[e[i]] == 0){// 刪除當前點與後繼結點的邊,如果刪除後
//其後繼結點的入度變爲0,就入隊
q.add(e[i]);
ans.add(e[i]);
}
}
}
if(ans.size() == n) return true;
else return false;
}
public static void main(String[] args) throws IOException{
String[] s = in.readLine().split(" ");
n = Int(s[0]);
m = Int(s[1]);
for(int i = 0; i < m; i++){
String s1[] = in.readLine().split(" ");
add(Int(s1[0]), Int(s1[1]));
d[Int(s1[1])] ++; // 入度加一
}
int flag = 0;
for(int i = 1; i <= n; i++){
if(d[i] == 0){ // 找到入度爲0的點
q.add(i);
ans.add(i);
flag = 1;
}
}
if(flag == 0)
out.write("-1\n");
else
{
if(bfs()){ // 輸出拓撲序
while(!ans.isEmpty()){
out.write(ans.poll()+" ");
}
}
else{ // 不存在拓撲序
out.write("-1\n");
}
}
out.flush();
}
}