拓扑排序(Topology_Sort)
基本思想
對一個有向無環圖(Directed Acyclic Graph簡稱DAG)G進行拓撲排序,是將G中所有頂點排成一個線性序列,使得圖中任意一對頂點u和v,若邊(u,v)∈E(G),則u在線性序列中出現在v之前。通常,這樣的線性序列稱為滿足拓撲次序(Topological Order)的序列,簡稱拓撲序列。簡單的說,由某個集合上的一個偏序得到該集合上的一個全序,這個操作稱之為拓撲排序。
算法原理
在圖論中,拓撲排序(Topological Sorting)是一個有向無環圖(DAG, Directed Acyclic Graph)的所有頂點的線性序列。且該序列必須滿足下面兩個條件:
每個頂點出現且只出現一次。
若存在一條從頂點 A 到頂點 B 的路徑,那么在序列中頂點 A 出現在頂點 B 的前面。
有向無環圖(DAG)才有拓撲排序,非DAG圖沒有拓撲排序一說。
例如,下面這個圖:
?
它是一個 DAG 圖,那么如何寫出它的拓撲排序呢?這里說一種比較常用的方法:
從 DAG 圖中選擇一個 沒有前驅(即入度為0)的頂點并輸出。
從圖中刪除該頂點和所有以它為起點的有向邊。
重復 1 和 2 直到當前的 DAG 圖為空或當前圖中不存在無前驅的頂點為止。后一種情況說明有向圖中必然存在環。
?
于是,得到拓撲排序后的結果是 { 1, 2, 4, 3, 5 }。
通常,一個有向無環圖可以有一個或多個拓撲排序序列。
應用
拓撲排序通常用來“排序”具有依賴關系的任務。
比如,如果用一個DAG圖來表示一個工程,其中每個頂點表示工程中的一個任務,用有向邊<A,B>表示在做任務 B 之前必須先完成任務 A。故在這個工程中,任意兩個任務要么具有確定的先后關系,要么是沒有關系,絕對不存在互相矛盾的關系(即環路)。
源代碼
C++版本一
//b[]為每個點的入度 for(i=1;i<=n;i++){for(j=1;j<=n;j++){if(b[j]==0){ //找到一個入度為0的點ans=j;vis[cnt++]=j;b[j]--;break;}}for(j=1;j<=n;j++)if(a[ans][j]) b[j]--; //與入度為0的點相連的點的入度減一 }printf("%d",vis[0]);for(i=1;i<cnt;i++) printf(" %d",vis[i]);printf("\n");C++版本二
queue<int>q;for(int i=0;i<n;i++) //n 節點的總數if(in[i]==0) q.push(i); //將入度為0的點入隊列vector<int>ans; //ans 為拓撲序列while(!q.empty()){int p=q.top(); q.pop(); // 選一個入度為0的點,出隊列ans.push_back(p);for(int i=0;i<edge[p].size();i++){int y=edge[p][i];in[y]--;if(in[y]==0)q.push(y); }}if(ans.size()==n) {for(int i=0;i<ans.size();i++)printf( "%d ",ans[i] );printf("\n");}else printf("No Answer!\n"); // ans 中的長度與n不相等,就說明無拓撲序列C++版本三
#include<iostream> #include <list> #include <queue> using namespace std;/************************類聲明************************/ class Graph {int V; // 頂點個數list<int> *adj; // 鄰接表queue<int> q; // 維護一個入度為0的頂點的集合int* indegree; // 記錄每個頂點的入度 public:Graph(int V); // 構造函數~Graph(); // 析構函數void addEdge(int v, int w); // 添加邊bool topological_sort(); // 拓撲排序 };/************************類定義************************/ Graph::Graph(int V) {this->V = V;adj = new list<int>[V];indegree = new int[V]; // 入度全部初始化為0for(int i=0; i<V; ++i)indegree[i] = 0; }Graph::~Graph() {delete [] adj;delete [] indegree; }void Graph::addEdge(int v, int w) {adj[v].push_back(w); ++indegree[w]; }bool Graph::topological_sort() {for(int i=0; i<V; ++i)if(indegree[i] == 0)q.push(i); // 將所有入度為0的頂點入隊int count = 0; // 計數,記錄當前已經輸出的頂點數 while(!q.empty()){int v = q.front(); // 從隊列中取出一個頂點q.pop();cout << v << " "; // 輸出該頂點++count;// 將所有v指向的頂點的入度減1,并將入度減為0的頂點入棧list<int>::iterator beg = adj[v].begin();for( ; beg!=adj[v].end(); ++beg)if(!(--indegree[*beg]))q.push(*beg); // 若入度為0,則入棧}if(count < V)return false; // 沒有輸出全部頂點,有向圖中有回路elsereturn true; // 拓撲排序成功 }
例題
http://acm.hdu.edu.cn/showproblem.php?pid=4857
參考文章
https://blog.csdn.net/qq_41713256/article/details/80805338
https://blog.csdn.net/lisonglisonglisong/article/details/45543451
總結
以上是生活随笔為你收集整理的拓扑排序(Topology_Sort)的全部內容,希望文章能夠幫你解決所遇到的問題。