拓撲排序-AOV網

簡介:

對一個有向無環圖(Directed Acyclic Graph簡稱DAG)G進行拓撲排序,是將G中所有頂點排成一個線性序列,使得圖中任意一對頂點u和v,若邊<u,v>∈E(G),則u在線性序列中出現在v之前。通常,這樣的線性序列稱爲滿足拓撲次序(Topological Order)的序列,簡稱拓撲序列。簡單的說,由某個集合上的一個偏序得到該集合上的一個全序,這個操作稱之爲拓撲排序。如下圖所示。
在這裏插入圖片描述

排序過程:

觀察上圖可以發現,每一個頂點都不會通過邊指向自己的前驅頂點。

  1. 在有向圖中,選取一個無前驅頂點且輸出它
  2. 刪除此頂點以及該頂點與其他頂點的連接
  3. 重複1,2步驟,直至不存在無前驅的頂點
算法實現:

爲了不破壞圖之間的關係,我們使用一個數組記錄每個頂點的入度(入度表明頂點的前驅個數)。通過棧記錄無前驅的頂點,倘若棧無記錄則不存在無前驅的頂點。這裏使用棧的原因是爲了避免重複掃描記錄入度的數組,因此使用隊列同樣可以達到目的。如果想記錄拓撲排序的頂點序號,則需再增設一個數組。

實現步驟:
  1. 首先獲取各頂點入度信息
  2. 若存在無前驅(入度=0)的頂點,則入棧
  3. 若棧不爲空,則刪除當前棧頂元素與其鄰接點之間的連接,更新記錄入度信息的數組
  4. 若存在入度爲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-網是有向無環圖)。排序過程中只有無前驅的節點纔會被記錄,若存在環,則不會被記錄。因此最終比較數組和頂點的大小便可確定是否存在環。
此外,通過打印記錄數組可以發現,使用棧和隊列的記錄順序是不同的。正確性毋庸置疑,因此可以得出拓撲排序序列不唯一的結論

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章