【圖論】拓撲排序

前言

在正文開始前,咱們先來了解一下有向無環圖(Directed Acyclic Graph簡稱DAG)算法

以下圖就是一個DAG圖,DAG圖是咱們討論拓撲排序的基礎。jsp

image

AOV網:數據在頂點 能夠理解爲面向對象
AOE網:數據在邊上,能夠理解爲面向過程!

1. 什麼是拓撲排序

拓撲排序(Topological Order),不少人據說過,可是不瞭解的一種算法。或許不少人只知道它是圖論的一種排序,至於幹什麼的不清楚。又或許不少人可能還會認爲它是一種啥排序。學習

而實質上它只是將DAG圖的頂點排成一個線性序列,獲得一個頂點的全序集合。其排序的順序依據就是節點的指向關係。好比前言的DAG圖:優化

  • ...
  • 節點5在節點4和節點3的後面
  • 節點9在節點6和節點7的後面
  • ...

那麼最後獲得的節點的線性序列結果,也必定要知足上面的指向順序。spa

每個節點都擁有入度(有多少點導向它,也就是開始它有多少前提)和出度(它導向多少點,也就是它是多少其餘節點開始的前提)。例如節點5的入度爲3和4,出度爲7。設計

拓撲排序的結果不是惟一的,只要符合上面的條件,那麼它就是拓撲序列,好比1 2 4 3 6 5 7 92 1 3 4 5 6 7 9,這兩個結果都是可行的。code

官方一點的定義:將有向圖中的節點以線性方式進行排序。即對於任何鏈接自節點u到節點v的有向邊uv,在最後的排序結果中,節點u老是在節點v的前面。

2. 現實案例

看了上面關於拓撲排序的概念若是還以爲十分抽象的話,那麼不妨考慮一個很是很是經典的例子——選課。對象

假設我很是想學習一門《jsp入門》的課程,可是在修這麼課程以前,咱們必需要學習一些基礎課程,好比《JAVA語言程序設計》,《HTML指南》等等。那麼這個制定選修課程順序的過程,實際上就是一個拓撲排序的過程,每門課程至關於有向圖中的一個頂點,而鏈接頂點之間的有向邊就是課程學習的前後關係。排序

只不過這個過程不是那麼複雜,從而很天然的在咱們的大腦中完成了。將這個過程以算法的形式描述出來的結果,就是拓撲排序。遞歸

image

能夠看到,上圖中的學習順序,就是拓撲序列,其不止一個結果。

拓撲排序算法在工程學中十分重要。

節點成環的圖,沒法被拓撲排序,由於這在工程上自己沒有意義,好比A——>B——>C——>A,那麼這個工程永遠沒法被開始。

3. 算法實現

拓撲排序的最優時間複雜度是O(m+n),其中m和n是DAG圖中節點數和邊數。由於拓撲排序至少要對DAG圖的節點和邊進行一次完整的遍歷。

拓撲排序的最優空間複雜度是O(m+n),其中m和n是DAG圖中節點數和邊數。咱們通常使用鄰接表來存儲DAG圖,所以空間複雜度是O(m+n)。

3.1 廣度優先搜索法(BFS)

3.1.1 BFS實現拓撲排序

廣度優先搜索法的思路很簡單:

  1. 從DAG圖中找到入度爲0的節點A(也就是沒有箭頭指向它的節點),將其放入拓撲序列的結果集。
  2. 同時刪除由節點A出發的全部邊。
  3. 在剩下的DAG圖中重複1-2兩步。
  4. 若是最後能夠把所有的節點都刪除並加入到結果集,那表示DAG圖能夠被拓撲排序;不然,若是最後有節點被剩下,那說明該圖是有環圖,沒法被拓撲排序。

以下圖

image

image

image

image

3.1.2 BFS實現拓撲排序的優化

若是有時候,咱們只須要知道某個DAG圖是否能夠拓撲排序,而不須要真正獲得拓撲排序後的結果,那麼能夠不須要結果集列表,只須要統計被刪除的節點的數量便可,若是該數量等於DAG圖的節點數,那麼DAG圖能夠被拓撲排序

3.2 深度優先搜索法(DFS)

3.2.1 DFS實現拓撲排序

深度優先搜索法是廣度優先搜索法的逆向思路,它的步驟以下:

  1. 選取圖中任意一個節點A,將其狀態標記爲「搜索中」
  2. 尋找節點A的鄰接點(沿着箭頭指向尋找相鄰的節點)

    1. 若是A存在鄰接點

      1. 若是A的鄰接點中存在狀態爲「搜索中」的鄰接點,那麼表示DAG圖有環路,不可拓撲排序。
      2. 不然,那麼任意選擇一個狀態爲「未搜索」的鄰接點B,使用遞歸對B重複作1和2操做,注意此時B的鄰接點判斷不包含來路(也就是A節點)。等到A的全部鄰接點都被搜索到,遞歸回溯回A節點的時候,那麼A節點也會被標記爲「已搜索」,並壓入結果棧。
    2. 若是A不存在鄰接點,那麼將節點A的狀態改成「已完成」,而且將其壓入一個結果集的棧中。
  3. A節點及其相鄰節點都搜索完畢後,若是還有未搜索的節點,那麼任意選取一個節點當作出發點,繼續重複1,2,3步驟。
  4. 直到全部的節點都被搜索並壓入棧,那麼此時結果棧中,從棧頂到棧底的順序,就是拓撲排序的順序。

3.2.2 DFS實現拓撲排序的優化

若是有時候,咱們只須要知道某個DAG圖是否能夠拓撲排序,而不須要真正獲得拓撲排序後的結果,那麼能夠不須要結果棧,只須要判斷整個深度優先搜索過程,沒有發生「搜索中」節點的相鄰節點(不包含來路的節點)也是「搜索中」就行。

相關文章
相關標籤/搜索