數據結構與算法之圖(三)圖的拓撲排序

引言

工程中嘗嘗氛圍很多步驟完成,有些步驟可以同步進行,而有些步驟需要某些步驟完成后才能進行,如何安排它們的流程是項目實施要考慮的重要問題,這就是拓撲排序問題。拓撲排序,它可以決定哪些子工程必須要先執行,哪些子工程要在某些工程執行后才可以執行。為了形象地反映出整個工程中各個子工程(活動)之間的先后關系,可用一個有向圖來表示,圖中的頂點代表活動(子工程),圖中的有向邊代表活動的先后關系,即有向邊的起點的活動是終點活動的前序活動,只有當起點活動完成之后,其終點活動才能進行。通常,我們把這種頂點表示活動、邊表示活動間先后關系的有向圖稱做頂點活動網(Activity On Vertex network),簡稱AOV網。
一個AOV網應該是一個有向無環圖,即不應該帶有回路,因為若帶有回路,則回路上的所有活動都無法進行(對于數據流來說就是死循環)。在AOV網中,若不存在回路,則所有活動可排列成一個線性序列,使得每個活動的所有前驅活動都排在該活動的前面,我們把此序列叫做拓撲序列(Topological order),由AOV網構造拓撲序列的過程叫做拓撲排序(Topological sort)。AOV網的拓撲序列不是唯一的,滿足上述定義的任一線性序列都稱作它的拓撲序列。

拓撲排序思想

1.在有向圖中選一個沒有前驅的頂點并且輸出;
2.從圖中刪除該頂點和所有以它為尾的?。ò自捑褪牵簞h除所有和它有關的邊);
3.重復上述兩步,直至所有頂點輸出,或者當前圖中不存在無前驅的頂點為止,后者代表我們的有向圖是有環的,因此,也可以通過拓撲排序來判斷一個圖是否有環。
拓撲排序有點像抽絲剝繭,每一次遍歷都會去掉最外層的"殼",抽完為止。
圖解
1.針對下圖:



有入度0的頂點V1和V6,先把他們放入集合S=[V1,V6],這里隨機取出V6遍歷,S=[V1]。
2.V6遍歷完,去掉和V4、V5的邊(入度減1),圖結構如下:



3.從集合中取出V1,刪除邊,V2、V3、V4入度減一后,V4和V3入度為0,S=[V3,V4],圖結構如下:

4.從集合中取出V4或者V3(取哪一個看具體算法),這里取V4,V5入度為減一不為0,S為[V3],圖結構為:

5,從集合中取出V3,V2、V5入度為0,S=[V2,V5],圖結構如下:
image

6.繼續輸出集合中蒸魚頂點,最后的該圖的一個拓撲排序結果為:

v6—>v1—>v4—>v3—>v5—>v2。
注意:拓撲排序僅反應頂點間的層序關系,輸出結果不唯一。

代碼實現

代碼實現,我們引入一個集合(?;蛘哧犃芯?,存放未遍歷的入度為0的頂點;引入計數變量檢測圖是否遍歷完。具體實現如下:

package graphic;

import queue.Queue;

/**
 * Created by chenming on 2018/6/16
 * 拓撲排序
 */
public class TopologySort {
    private class VNode {
        int value;//值
        int inDegree;//入度
    }

    private int[][] map;//鄰接矩陣
    private VNode[] nodes;

    public TopologySort(int[][] map) {
        this.map = map;
        nodes = new VNode[map.length];
        for (int i = 0; i < map.length; i++) {
            //初始化入度
            VNode node = new VNode();
            node.value = i;
            node.inDegree = getIndegree(i);
            nodes[i] = node;
        }
    }

    /**
     * 從鄰接矩陣中得到頂點入度
     *
     * @param index
     * @return
     */
    private int getIndegree(int index) {
        int in = 0;
        for (int i = 0; i < map.length; i++) {
            if (0 < map[i][index] && map[i][index] < Integer.MAX_VALUE) {
                in++;
            }
        }
        return in;
    }

    /**
     * 有向圖的拓撲排序,,思路如下:
     * 1,類似廣度優先遍歷,引入隊列保存入度為0的未遍歷節點
     * 2.每去掉一個頂點,則去掉該頂點的邊,即它所有鄰接頂點的頂點入度-1,如果為0則加入隊列
     * 3.循環執行步驟2,直到隊列為空
     * 4.循環結束檢測是否遍歷完整,如果沒有遍歷完全,則表示有回環
     */
    public void topologySort() {
        Queue<Integer> queue = new Queue<>();//保存入度為0的索引
        int numb = map.length;
        int count = 0;//校驗用
        //收集入度為0的頂點
        for (int i = 0; i < numb; i++) {
            if (nodes[i].inDegree == 0) {
                queue.enquene(i);
            }
        }

        while (!queue.isEmpty()) {
            int index = queue.dequeue();
            System.out.println("拓撲排序:" + index);
            count++;
            //遍歷完"刪除"頂點index的所有邊,即減少它鄰接頂點的入度
            for (int i = 0; i < numb; i++){
                if(map[index][i] > 0 && map[index][i] < Integer.MAX_VALUE){
                    if(--nodes[i].inDegree == 0){
                        //入度減一后,加入隊列
                        queue.enquene(i);
                    }

                }
            }

        }

        if(count != numb){
            throw new RuntimeException("圖中存在回環");
        }

    }
}

測試圖用例:


拓撲排序用例.png

測試代碼:

   @Test
    public void testTopol() {
        int INF = Integer.MAX_VALUE;
        int[][] map = {
                {0, INF, INF, INF, 1, 1, INF, INF, INF, INF, INF, 1, INF, INF},//0
                {INF, 0, 1, INF, 1, INF, INF, INF, 1, INF, INF, INF, INF, INF},//1
                {INF, INF, 0, INF, INF, 1, 1, INF, INF, 1, INF, INF, INF, INF},//2
                {INF, INF, 1, 0, INF, INF, INF, INF, INF, INF, INF, INF, INF, 1},//3
                {INF, INF, INF, INF, 0, INF, INF, 1, INF, INF, INF, INF, INF, INF},//4
                {INF, INF, INF, INF, INF, 0, INF, INF, 1, INF, INF, INF, 1, INF},//5
                {INF, INF, INF, INF, INF, 1, 0, INF, INF, INF, INF, INF, INF, INF},//6
                {INF, INF, INF, INF, INF, INF, INF, 0, INF, INF, INF, INF, INF, INF},//7
                {INF, INF, INF, INF, INF, INF, INF, 1, 0, INF, INF, INF, INF, INF},//8
                {INF, INF, INF, INF, INF, INF, INF, INF, INF, 0, 1, 1, INF, INF},//9
                {INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, 0, INF, INF, 1},//10
                {INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, 0, INF, INF},//11
                {INF, INF, INF, INF, INF, INF, INF, INF, INF, 1, INF, INF, 0, INF},//12
                {INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, 0},//13
        };
        TopologySort ts = new TopologySort(map);
        ts.topologySort();
    }

輸出:

拓撲排序:0
拓撲排序:1
拓撲排序:3
拓撲排序:4
拓撲排序:2
拓撲排序:6
拓撲排序:5
拓撲排序:8
拓撲排序:12
拓撲排序:7
拓撲排序:9
拓撲排序:10
拓撲排序:11
拓撲排序:13

完整代碼地址:數據結構與算法學習JAVA描述GayHub地址

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,002評論 6 542
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,400評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,136評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,714評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,452評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,818評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,812評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,997評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,552評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,292評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,510評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,035評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,721評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,121評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,429評論 1 294
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,235評論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,480評論 2 379

推薦閱讀更多精彩內容

  • 一些概念 數據結構就是研究數據的邏輯結構和物理結構以及它們之間相互關系,并對這種結構定義相應的運算,而且確保經過這...
    Winterfell_Z閱讀 5,885評論 0 13
  • 第一章 緒論 什么是數據結構? 數據結構的定義:數據結構是相互之間存在一種或多種特定關系的數據元素的集合。 第二章...
    SeanCheney閱讀 5,803評論 0 19
  • 數據結構學不好,c++就到后面會很迷,數據結構真滴很重要啊,上機題一定要認真做,緊密的和實際操作的代碼聯系在一起是...
    Nancy_Shi閱讀 734評論 0 4
  • https://zh.visualgo.net/graphds 淺談圖形結構https://zh.visualgo...
    狼之獨步閱讀 4,186評論 0 0
  • 課程介紹 先修課:概率統計,程序設計實習,集合論與圖論 后續課:算法分析與設計,編譯原理,操作系統,數據庫概論,人...
    ShellyWhen閱讀 2,337評論 0 3