C#實現有向無環圖(DAG)拓撲排序


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

線性結構概念

總的來說,“線性結構”是一個有序數據元素的集合 線性結構滿足以下特點:

  1. 集合中必存在唯一“第一個元素”;
  2. 集合中必存在唯一“最后一個元素”;
  3. 除了最后一個元素,所有元素均有唯一“后繼結點”;
  4. 除了第一個元素,所有元素均有唯一“前趨結點”

和我們abp Module很像,第一個加載模塊永遠是其ABP核心模塊,最后一個模塊永遠是我們的啟動模塊

舉例

1.大學課程排序
大學課程的學習是有先后順序的,C語言是基礎,數據結構依賴於C語言,其它課程也有類似依賴關系。這樣的一個課程安排是怎么實現的呢?

2.VS項目編譯順序
假設VS中有三個項目A,B,C,它們的關系如下。VS編譯器是如何判斷三個項目的編譯順序的呢?
A->B->C A引用B B引用C
A->B->C->A 提示循環引用

ABP的Module

ABP中的模塊也是如此,不可循環引用相互依賴A->B B->A X
前面說到ABP中的第一個模塊和最后一個模塊是確定的。
呢么中間的是怎么排序的呢。其實用的是拓撲算法
模塊圖
從圖中可以得知:
1.A模塊是最核心的,不依賴於其他任何模塊
2.D依賴E和B,E依賴B和C,B依賴C和A,C依賴A
那么根據拓撲排序,應該如何排序呢?
1.從圖中找一個沒有前驅指向它的頂點
2.刪除該頂點.以及該頂點的前驅
3.重復步驟 1 and 2 ,直到圖中頂點為空 或者 找不到步驟1中這樣的頂點 為止.
排序如下:
排序
結果就是D->E->B->C->A 排完之后正好對應D依賴E和B,E依賴B和C,B依賴C和A,C依賴A
這個順序在ABP的模塊這看來是行不通的,需要在反轉一次,最先加載A,才行。

C#實現深度優先搜索

有這樣一個DAG圖

如果對它進行排序的話,其實過程是這樣的.
圖中,頂點A是沒有指向它的前驅的,所以從它開始訪問

1.訪問 A
2.訪問 B
3.訪問 C
在訪問了 B 后應該是訪問 B 的另外一個頂點,這里可以是隨機的也可以是有序的,具體取決於你存儲的序列順序,這里先訪問 C 。
4.訪問 E
5.訪問 D
這里訪問 D 是因為 B 已經被訪問過了,所以訪問頂點 D 。
6.訪問 F
因為頂點 C 已經被訪問過,所以應該回溯訪問頂點 B 的另一個有向邊指向的頂點 F 。
7.訪問 G
那么代碼應該如何寫呢?
source:需要排序的集合
getDepends:一個func委托,用於獲取當前模塊依賴的其他模塊
方法內部維護了一個字典對象Visited 用於存儲已經訪問過的模塊,key表示模塊,value是一個bool,true時表示正在處理,false表示以及處理完成,
處理完成的模塊會加入到sorted集合中

static List<T> MySort<T>(IEnumerable<T> source, Func<T, IEnumerable<T>> getDepends)
{
    // 訪問過的路徑
    Dictionary<T, bool> visited = new Dictionary<T, bool>();
    // 已經排過序的
    List<T> sorted = new List<T>();
    foreach (var item in source)
    {
        Visit<T>(item, getDepends, visited, sorted);
    }
    return sorted;
}

static void Visit<T>(T item, Func<T, IEnumerable<T>> getDepends, Dictionary<T, bool> visited, List<T> sorted)
{
    //已經訪問過了
    if (visited.ContainsKey(item))
    {
        bool isVisit = visited[item];
        if (isVisit == true)
        {
            throw new Exception("循環引用");
        }
    }
    //未訪問
    else
    {
        visited.Add(item, true);//true :正在訪問 false:訪問完成

        //獲取所有依賴
        var depends = getDepends(item);
        foreach (var depend in depends)
        {
            Visit(depend, getDepends, visited, sorted);
        }

        //訪問完成
        visited[item] = false;
        sorted.Add(item);
    }

}

完整demo github


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM