日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

图Graph--拓扑排序(Topological Sorting)

發布時間:2024/7/5 编程问答 42 豆豆
生活随笔 收集整理的這篇文章主要介紹了 图Graph--拓扑排序(Topological Sorting) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章目錄

    • 1. 拓撲排序
    • 2. 算法實現
      • 2.1 Kahn算法
      • 2.2 DFS算法
      • 2.3 時間復雜度
    • 3. 應用
    • 4. 類似題目練習

一個項目往往會包含很多代碼源文件。編譯器在編譯整個項目時,需按照依賴關系,依次編譯每個源文件。比如,A.cpp依賴B.cpp,那在編譯時,編譯器需要先編譯B.cpp,才能編譯A.cpp。
編譯器通過分析源文件或者編譯配置文件(比如Makefile文件),來獲取這種局部的依賴關系。那編譯器又該如何通過源文件兩兩之間的局部依賴關系,確定一個全局的編譯順序呢?

1. 拓撲排序

  • 可以把源文件與源文件之間的依賴關系,抽象成一個有向圖。每個源文件對應圖中的一個頂點,源文件之間的依賴關系就是頂點之間的邊。
  • 如果a先于b執行,也就是說b依賴于a,那么就在頂點a和頂點b之間,構建一條從a指向b的邊。而且,這個圖不僅要是有向圖,還要是一個有向無環圖,也就是不能存在像a->b->c->a這樣的循環依賴關系。

數據結構如下:

#include <list> using namespace std; class Graph {int v;//頂點個數list<int> *adj;//鄰接表 public:Graph(int vn){v = vn;adj = new list<int> [v];}~Graph(){delete [] adj;}void addEdge(int s, int t)//s先于t,邊s->t{adj[s].push_back(t);} };

2. 算法實現

2.1 Kahn算法

  • Kahn 算法是貪心思想
  • 如果 s 需要先于 t 執行,就添加一條 s 指向 t 的邊。如果某個頂點入度為0,也就表示,沒有任何頂點必須先于這個頂點執行,那么這個頂點就可以執行了。
  • 先從圖中,找出一個入度為0的頂點,將其輸出,并刪除這個頂點(也就是把這個頂點可達的頂點的入度都減1)。我們循環執行上面的過程,直到所有的頂點都被輸出。最后輸出的序列,就是滿足局部依賴關系的拓撲排序。
/*** @description: 拓撲排序,有向無環圖* @author: michael ming* @date: 2019/7/29 0:36* @modified by: */ #include <list> #include <iostream> #include <queue>using namespace std; class G_Node //節點類 { public:char info;//節點存儲信息int indegree;//節點入度G_Node(char ch = '/'):info(ch),indegree(0){}; }; class Graph //圖類 {int v; //頂點個數list<G_Node*> *adj; //鄰接表G_Node *pGNode;//節點 public:Graph(int vn){v = vn;adj = new list<G_Node*> [v];pGNode = new G_Node [v];cout << "請順序輸入節點的信息:" << endl;char ch;for(int i = 0; i < v; ++i)cin >> pGNode[i].info;}~Graph(){delete [] pGNode;delete [] adj;}int findIdx(char ch){for(int i = 0; i < v; ++i){if(pGNode[i].info == ch)return i;}return -1;}void addEdge(char s, char t)//s先于t,邊s->t{int i = findIdx(s), j = findIdx(t);if(i != -1 && j != -1){adj[i].push_back(&pGNode[j]);pGNode[j].indegree++;}}void topoSortByKahn(){int i, j, k;queue<G_Node*> nodeQueue;//坑,要存指針在里面,后面才能修改入度,否則修改的是副本G_Node *frontNode;list<G_Node*>::iterator it;for(i = 0; i < v; ++i){if(pGNode[i].indegree == 0)nodeQueue.push(&pGNode[i]);//找到所有入度為0的入隊}while(!nodeQueue.empty()){frontNode = nodeQueue.front();i = findIdx(frontNode->info);nodeQueue.pop();cout << frontNode->info << "->";//輸出入度為0的,出隊for(it = adj[i].begin(); it != adj[i].end(); ++it){(*it)->indegree--;//該節點后面跟著的所有節點入度-1if((*it)->indegree == 0)//入度如果等于0nodeQueue.push(*it);//入隊,一會可以打印了}}} }; int main() {Graph grp(6);grp.addEdge('a','b');grp.addEdge('b','e');grp.addEdge('b','d');grp.addEdge('d','c');grp.addEdge('d','f');grp.topoSortByKahn();return 0; }

2.2 DFS算法

  • 構造逆鄰接表。鄰接表中,邊 s->t 表示 s 先于 t 執行,也就是 t 要依賴 s。在逆鄰接表中,邊 s->t 表示 s 依賴于 t,s 后于 t 執行。
  • 遞歸處理每個頂點。對頂點 i ,先輸出它可達的所有頂點,也就是,先把它依賴的所有的頂點輸出了,然后再輸出自己。

在上面程序代碼中添加

list<G_Node*> *reverseadj; //逆鄰接表 reverseadj = new list<G_Node*> [v]; void addEdge(char s, char t)//s先于t,邊s->t {int i = findIdx(s), j = findIdx(t);if(i != -1 && j != -1){adj[i].push_back(&pGNode[j]);//s->t,鄰接表pGNode[j].indegree++;reverseadj[j].push_back(&pGNode[i]);//逆鄰接表}} void topoSortByDFS() {cout << "topoSortByDFS:" << endl;bool *visited = new bool [v];memset(visited,0,v*sizeof(bool));for(int i = 0; i < v; ++i) //深度優先遍歷{if(visited[i] == false){visited[i] = true;dfs(i, reverseadj, visited);}}delete [] visited; } void dfs(int i, list<G_Node*> *reverseadj, bool *visited) {int idx;for(auto it = reverseadj[i].begin(); it != reverseadj[i].end(); ++it){idx = findIdx((*it)->info);if(visited[idx] == true)continue;visited[idx] = true;dfs(idx,reverseadj,visited);}cout << pGNode[i].info << "->"; }

2.3 時間復雜度

  • Kahn代碼中,每個頂點被訪問了一次,每個邊也都被訪問了一次,所以,Kahn算法的時間復雜度就是O(V+E)(V表示頂點個數,E表示邊的個數)。
  • DFS算法中,每個頂點被訪問兩次,每條邊都被訪問一次,所以時間復雜度也是O(V+E)。
  • 注意,這里的圖可能不是連通的,有可能是有好幾個不連通的子圖構成,所以,E并不一定大于V,V E的大小關系不定。所以,在表示時間復雜度的時候,V、E都要考慮在內。

3. 應用

  • 拓撲排序應用非常廣泛。凡是需要通過局部順序推導全局順序的,一般都能用拓撲排序來解決。
  • 拓撲排序還能檢測圖中環的存在。對于Kahn算法來說,如果最后輸出出來的頂點個數,少于圖中頂點個數,圖中還有入度不是0的頂點,那就說明,圖中存在環。
  • 關于圖中環的檢測,遞歸那節講過一個例子,在查找最終推薦人的時候,可能會因為臟數據,造成存在循環推薦,比如,用戶A推薦了用戶B,用戶B推薦了用戶C,用戶C又推薦了用戶A。如何避免這種臟數據導致的無限遞歸?
    這就是環的檢測問題。因為我們每次都只是查找一個用戶的最終推薦人,所以,我們并不需要動用復雜的拓撲排序算法,而只需要記錄已經訪問過的用戶ID,當用戶ID第二次被訪問的時候,就說明存在環。

4. 類似題目練習

LeetCode 207. 課程表(拓撲排序)
LeetCode 210. 課程表 II(拓撲排序)
LeetCode 269. 火星詞典(拓撲排序)
LeetCode 851. 喧鬧和富有(拓撲排序)
LeetCode 1136. 平行課程(拓撲排序)
LeetCode 1203. 項目管理(兩次拓撲排序)
LeetCode 5665. 從相鄰元素對還原數組(拓撲排序)
LeetCode 5699. 從第一個節點出發到最后一個節點的受限路徑數(迪杰斯特拉 + 拓撲排序)

總結

以上是生活随笔為你收集整理的图Graph--拓扑排序(Topological Sorting)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。