程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> 學習總結---拓撲排序

學習總結---拓撲排序

編輯:C++入門知識

拓撲排序什麼是拓撲序列
通常,這樣的線性序列稱為滿足拓撲次序(Topological Order)的序列,簡稱拓撲序列。簡單的說,由某個集合上的一個偏序得到該集合上的一個全序,這個操作稱之為拓撲排序。離散數學中關於偏序和全序的定義:
若集合X上的關系是R,且R是自反的、反對稱的和傳遞的,則稱R是集合X上的偏序關系。
設R是集合X上的偏序(Partial Order),如果對每個x,y屬於X必有xRy 或 yRx,則稱R是集合X上的全序關系。
比較簡單的理解:偏序是指集合中只有部分成員可以比較,全序是指集合中所有的成員之間均可以比較。
注意:
①若將圖中頂點按拓撲次序排成一行,則圖中所有的有向邊均是從左指向右的。
②若圖中存在有向環,則不可能使頂點滿足拓撲次序。
③一個DAG的拓撲序列通常表示某種方案切實可行。


昨天花了一天的時間研究了下拓撲排序,覺得學到很多,所以寫一下總結。

 


<1>有向圖的表示方式可以分成鄰接矩陣和鄰接表兩種。

鄰接矩陣是指用一個n*n的數組,下標為i,j的位置記錄點i和點j的關系。(適用與稀疏矩陣,對內存空間要求要比較高)

鄰接表是單純記錄關系的數組,比如 i和 j 有關系, 就將 j 放在 [i][k]的位置(k為當前與i有關系的點數),這種表示法適用於關系較少的時候,但是引用進STL中的vector以後,好像數據量的大小對其就沒有什麼太大的影響了。

<2>拓撲排序:每次找到入度為0 的點,並將其出度清空(可以用DFS或BFS),跟據表示的方法不同,操作也會有所差別。

<3>給出有向圖之後,會有存在拓撲排序和不存在拓撲排序之分,如果圖中存在有向環,則不存在拓撲排序(就是在遍歷的過程中直接和間接的相互指向),在處理上只要記錄每個點的訪問狀態就可以了,是正在訪問(不滿足的情況),還是未訪問,或是已經訪問過(DFS)。

如果存在了拓撲排序,也會有拓撲排序唯不唯一的問題(如果沒有要求考慮這點,用DFS會號做一些),這點其實也好處理,只要每次入度為0的點只有一個才可以(BFS)。

DFS  +  鄰接表
 

DFS + 鄰接矩陣。  
int vis[MAXN]; 
int topo[MAXN], cnt; 
bool DFS(int u){ 
    vis[u] = -1;    //表示當前正在訪問,如果再次訪問說明不存在topo排序。  
    for (int i = 0; i < n; i++){ 
        if (G[u][i]){ 
            if (vis[i] < 0) 
                return false;   //存在有向環,失敗退出。  
            else if (!vis[i] && !DFS(i)) 
                return false; 
        } 
    } 
    vis[u] = 1; 
    topo[--cnt] = u; 
    return true;    // 成功記錄,返回true 。  
} 
 
bool toposort(){ 
    cnt = n; 
    memset(vis, 0, sizeof(vis));    // 如果要考慮排序是否唯一,這邊每次都需將所有點遍歷過一遍,且每次只可有一點的入度為0。  
    for (int u = 0; u < n; u++) 
        if (!DFS(u)) 
            return false; 
    return true; 
} 

//  DFS + 鄰接矩陣。
int vis[MAXN];
int topo[MAXN], cnt;
bool DFS(int u){
 vis[u] = -1; //表示當前正在訪問,如果再次訪問說明不存在topo排序。
 for (int i = 0; i < n; i++){
  if (G[u][i]){
   if (vis[i] < 0)
    return false; //存在有向環,失敗退出。
   else if (!vis[i] && !DFS(i))
    return false;
  }
 }
 vis[u] = 1;
 topo[--cnt] = u;
 return true; // 成功記錄,返回true 。
}

bool toposort(){
 cnt = n;
 memset(vis, 0, sizeof(vis)); // 如果要考慮排序是否唯一,這邊每次都需將所有點遍歷過一遍,且每次只可有一點的入度為0。
 for (int u = 0; u < n; u++)
  if (!DFS(u))
   return false;
 return true;
}BFS(借助STL-queue) + 鄰接表(借助STL-vector)




// BFS + 鄰接表。  
vector<int> G[MAXN];  // 鄰接表。  
int son[MAXN];          // 入度數。  
 
void topo(){ 
    queue<int> que; 
    int ok = 0; 
    for (int i = 0; i < n; i++) 
        if (!son[i]) 
            que.push(i);    // 入度為0時入隊。  
 
    while (!que.empty()){ 
        if (que.size() > 1) 
            ok = 1;         // 當隊列中個數超多1時,表示有不唯一解。  
        int t = que.front(); 
        que.pop(); 
        cnt--;          //  如果隊列為空後,計數器> 0, 說明存在環結構。  
        for (int i = 0; i < G[t].size(); i++) 
            if (--son[G[t][i]] == 0) // 判斷減掉當前點的關系後,點的入度是否為0。  
                que.push(G[t][i]); 
    } 
} 

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved