簡單易懂的拓撲排序


1.定義

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

在AOV網中,若不存在回路,則所有活動可排列成一個線性序列,使得每個活動的所有前驅活動都排在該活動的前面,我們把此序列叫做拓撲序列(Topological order),由AOV網構造拓撲序列的過程叫做拓撲排序(Topological sort)。AOV網的拓撲序列不是唯一的,滿足上述定義的任一線性序列都稱作它的拓撲序列。 

2.拓撲排序的實現步驟

在有向圖中選一個沒有前驅的頂點並且輸出。

從圖中刪除該頂點和所有以它為尾的弧(白話就是:刪除所有和它有關的邊)。

重復上述兩步,直至所有頂點輸出,或者當前圖中不存在無前驅的頂點為止,后者代表我們的有向圖是有環的,因此,也可以通過拓撲排序來判斷一個圖是否有環。

3.拓撲排序實例手動實現

如果我們有如下的一個有向無環圖,我們需要對這個圖的頂點進行拓撲排序,過程如下:

 

首先,我們發現V6和v1是沒有前驅的,所以我們就隨機選去一個輸出,我們先輸出V6,刪除和V6有關的邊,得到如下圖結果:

 

然后,我們繼續尋找沒有前驅的頂點,發現V1沒有前驅,所以輸出V1,刪除和V1有關的邊,得到下圖的結果:

 

然后,我們又發現V4和V3都是沒有前驅的,那么我們就隨機選取一個頂點輸出(具體看你實現的算法和圖存儲結構),我們輸出V4,得到如下圖結果:

 

然后,我們輸出沒有前驅的頂點V3,得到如下結果:

 

然后,我們分別輸出V5和V2,最后全部頂點輸出完成,該圖的一個拓撲序列為:

v6–>v1—->v4—>v3—>v5—>v2

過程簡述:

從 DAG 圖中選擇一個 沒有前驅(即入度為0)的頂點並輸出。

從圖中刪除該頂點和所有以它為起點的有向邊。

重復 1 和 2 直到當前的 DAG 圖為空或當前圖中不存在無前驅的頂點為止。若當前圖中不存在無前驅的頂點說明有向圖中必存在環。

4.拓撲排序Java實現

(1)   示例

現在你總共有 n 門課需要選,記為 0 到 n-1。

在選修某些課程之前需要一些先修課程。 例如,想要學習課程 0 ,你需要先完成課程 1 ,我們用一個匹配來表示他們: [0,1]

給定課程總量以及它們的先決條件,判斷是否可能完成所有課程的學習?

示例 1:

輸入: 2, [[1,0]]

輸出: true

解釋: 總共有 2 門課程。學習課程 1 之前,你需要完成課程 0。所以這是可能的。

示例 2:

輸入: 2, [[1,0],[0,1]]

輸出: false

解釋: 總共有 2 門課程。學習課程 1 之前,你需要先完成​課程 0;並且學習課程 0 之前,你還應先完成課程 1。這是不可能的。

說明:

輸入的先決條件是由邊緣列表表示的圖形,而不是鄰接矩陣。詳情請參見圖的表示法。

你可以假定輸入的先決條件中沒有重復的邊。

(2)   截圖步驟:

本題思路:采用拓撲排序+廣度優先搜索的方式進行求解。

步驟一:對所有節點建立一個入度表times,times[i]表示第i個節點有多少個入度,(這里入度的意思是當實現此節點時,需要多少個前驅節點的支持)。

步驟二:將入度表中入度為0的節點添加到隊列que中。

步驟三:取出隊列第一個元素temp;並將times中,以該節點作為前驅節點的入度減一,並判斷入度是否等於0,如果等於0加入到隊列中。

步驟四:重復步驟三,知道隊列為空,判斷times中是否存在非0的節點,如果存在則說明有環,不存在說明無環。(其中隊列每次輸出的值就是拓排序的輸出值)

(3)   Java代碼:

class Solution {
    public boolean canFinish(int numCourses, int[][] prerequisites) {
       int []times=new int[numCourses];
        for(int i=0;i<prerequisites.length;i++) {
            int num=prerequisites[i][0];
            times[num]=times[num]+1;
        }
        Queue<Integer> que=new LinkedList<Integer>();
        for(int j=0;j<times.length;j++) {
            if(times[j]==0) {
                que.add(j);
            }
        }
        while(!que.isEmpty()) {
            int temp=que.poll();
            for(int m=0;m<prerequisites.length;m++) {
                if(prerequisites[m][1]==temp) {
                    times[prerequisites[m][0]]--;
                    if(times[prerequisites[m][0]]==0) {
                        que.add(prerequisites[m][0]);
                    }
                }
            }
        }
        for(int e=0;e<times.length;e++) {
            if(times[e]!=0) {
                return false;
            }
        }
        return true;
    }
}


免責聲明!

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



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