簡介:
對一個有向無環圖(Directed Acyclic Graph簡稱DAG)G進行拓撲排序,是將G中所有頂點排成一個線性序列,使得圖中任意一對頂點u和v,若邊<u,v>∈E(G),則u在線性序列中出現在v之前。通常,這樣的線性序列稱爲滿足拓撲次序(Topological Order)的序列,簡稱拓撲序列。簡單的說,由某個集合上的一個偏序得到該集合上的一個全序,這個操作稱之爲拓撲排序。如下圖所示。
排序過程:
觀察上圖可以發現,每一個頂點都不會通過邊指向自己的前驅頂點。
- 在有向圖中,選取一個無前驅頂點且輸出它
- 刪除此頂點以及該頂點與其他頂點的連接
- 重複1,2步驟,直至不存在無前驅的頂點
算法實現:
爲了不破壞圖之間的關係,我們使用一個數組記錄每個頂點的入度(入度表明頂點的前驅個數)。通過棧記錄無前驅的頂點,倘若棧無記錄則不存在無前驅的頂點。這裏使用棧的原因是爲了避免重複掃描記錄入度的數組,因此使用隊列同樣可以達到目的。如果想記錄拓撲排序的頂點序號,則需再增設一個數組。
實現步驟:
- 首先獲取各頂點入度信息
- 若存在無前驅(入度=0)的頂點,則入棧
- 若棧不爲空,則刪除當前棧頂元素與其鄰接點之間的連接,更新記錄入度信息的數組
- 若存在入度爲0的頂點,則入棧
代碼示例:
#include<iostream>
#include<stack>
#include<queue>
using namespace std;
#define MAX_SIZE 100
int graph[MAX_SIZE][MAX_SIZE];
int degree[MAX_SIZE];
int seq[MAX_SIZE];
void init_arry(){
for(int i = 0; i < MAX_SIZE; i++){
for(int j = 0; j < MAX_SIZE; j++)
graph[i][j] = 0;
}
}
void init_graph(int m){
for(int i = 0; i < m; i++){
int x, y;
cin >> x >> y;
graph[x][y] = 1;
}
}
void get_degree(int n){
for(int i = 1; i <= n; i++){
int sum = 0;
for(int j = 1; j <= n; j++){
sum += graph[j][i];
}
degree[i] = sum;
}
}
bool topology_sort(int n){
//stack<int> s;
queue<int> s;
int count = 0;
for(int i = 1; i <= n; i++){
if(degree[i] == 0)
s.push(i);
}
while(!s.empty()){
//int t = s.top();棧
//s.pop();
int t = s.front();//隊列
for(int i = 1; i <= n; i++){
if(graph[t][i] != 0){
degree[i]--;
if(degree[i] == 0)
s.push(i);
}
}
seq[count++] = t;
s.pop();
}
if(count == n) return true;
else return false;
}
int main(){
int n, m;
cin >> n >> m;
init_arry();
init_graph(m);
get_degree(n);
if(topology_sort(n)){
for(int i = 0; i < n; i++)
cout << seq[i] << " ";
}
return 0;
}
也可以通過記錄拓撲序列的數組查看圖中是否有環(AOV-網是有向無環圖)。排序過程中只有無前驅的節點纔會被記錄,若存在環,則不會被記錄。因此最終比較數組和頂點的大小便可確定是否存在環。
此外,通過打印記錄數組可以發現,使用棧和隊列的記錄順序是不同的。正確性毋庸置疑,因此可以得出拓撲排序序列不唯一的結論。