圖解:什麼是拓撲排序?

2021-03-02 數據結構與算法

今天景禹給你們談一談拓撲排序,乍一聽上去,感覺很高大上,但她的確很高大上,所以一起徵服她吧!在正式介紹拓撲排序之前,我們一起看一看必備基礎。拓撲排序基礎篇 總覺得書上的概念有點欠缺生動,但還是需要這些基礎的概念作為支撐。一個 無環的有向圖 稱為 有向無環圖(Directed Acycline Graph),簡稱 DAG 圖。(這不等於沒說嗎?)所以直接看圖。圖中最左邊的是有向樹,中間的是有向無環圖,最右則的是有向圖(因為圖中BED三個頂點之間構成一個有向環,ACEB也存在環路)。所有的工程或者某種流程都可以分為若干個小的工程或者階段,我們稱這些小的工程或階段為「活動」。打個比方,如何把一隻大象裝到冰箱裡,很簡單,分三步。第一,打開冰箱門;第二,將大象裝進去;第三,關上冰箱門。這三步中的每一步便是一個 「活動」 。在一個表示工程的有向圖中,用 頂點表示活動,用弧表示活動之間的優先關係的有向圖 稱為頂點表示活動的網(Activity On Vertex Network),簡稱AOV網。AOV網中的弧表示活動之間存在的某種制約關係,比如上面說到將大象裝入冰箱,必須先打開冰箱門,才能將大象裝進去,大象裝進去才能關上冰箱門,從而完成我們的任務。還有一個經典的例子那就是選課,通常我們是學了C語言程序設計,才能學習數據結構,這裡的制約關係就是課程之間的優先關係。(之後我們會講一個完整的例子,大家就會更清楚AOV網)設G=(V,E)是一個具有n個頂點的有向圖,V中的頂點序列 一個拓撲序列 ,整個過程便叫做 拓撲排序。有向有環圖其實過程和之前類似,只是給大家提供一個思路,如果面試官問你,如何判斷一個有向圖中是否存在環時,你應該第一反應想到的就是拓撲排序,為什麼拓撲排序可以判斷一個有向圖中是否存在環呢?我們看慄子。第一步:在有向圖中選擇一個沒有前驅的頂點並輸出;圖中沒有前驅的頂點為A。第三步:在有向圖中選擇一個沒有前驅的頂點並輸出;圖中沒有前驅的頂點為C。第五步:在有向圖中選擇一個沒有前驅的頂點並輸出;發現當前圖不存在無前驅的頂點,但拓撲序列中並未輸出所有的頂點,所以剩下的頂點構成了環,也證明了該有向圖存在環。拓撲排序實現篇 針對於拓撲排序思想篇提到的兩步操作,我們採用鄰接表作為有向圖的存儲結構,並且在頭結點中增加一個存放頂點入度的數組(indegree)。入度為零的頂點即為沒有前驅的頂點,刪除頂點及以它為尾的弧的操作,則則可換以弧頭頂點的入度減1來實現。為了避免重複檢查入度為零的頂點,可另設一個棧暫存所有入度為領的頂點。為了清晰地呈現拓撲排序的實現,我們還是以上面提到的有向無環圖為慄子進行講解,Step By Step。第一步:遍歷所有頂點,將入度為0的頂點入棧,即分別將頂點
// 拓撲排序算法
// 若GL無迴路,則輸出拓撲排序序列並返回OK,否則返回ERROR
Status TopologicalSort(GraphAdjList GL)
{
   EdgeNode *e;
   int i, k, gettop;
   int top = 0;  // 用於棧指針下標索引
   int count = 0;  // 用於統計輸出頂點的個數
   int *stack;   // 用於存儲入度為0的頂點
 
   stack = (int *)malloc(GL->numVertexes * sizeof(int));
 
   for( i=0; i < GL->numVertexes; i++ )
   {
      if( 0 == GL->adjList[i].in )
      {
         stack[++top] = i; // 將度為0的頂點下標入棧
      }
   }
 
   while( 0 != top )
   {
      gettop = stack[top--]; // 出棧
      printf("%d -> ", GL->adjList[gettop].data);
      count++;    
  
      for( e=GL->adjList[gettop].firstedge; e; e=e->next )
      {
         k = e->adjvex;
   // 注意:下邊這個if條件是分析整個程序的要點!
   // 將k號頂點鄰接點的入度-1,因為他的前驅已經消除
   // 接著判斷-1後入度是否為0,如果為0則也入棧
         if( !(--GL->adjList[k].in) ) 
         {
            stack[++top] = k;
         }
      }
   }
 
   if( count < GL->numVertexes ) // 如果count小於頂點數,說明存在環
   {
      return ERROR;
   }
   else
   {
      return OK;
   }
}

時間複雜度分析對於包含n個頂點和 e 條弧的有向圖而言,求各個頂點的入度的時間複雜度為 請你判斷是否可能完成所有課程的學習?  你的第一反應是否想到題目就相當於判斷一個有向圖中是否有環呢?我相信小禹禹一定想到了,比如示例二的輸入就表示有向圖中存在環,所以不能完成所有課程,如下圖形式:而示例一中就不存在環,即可以完成所有課程,如下圖所示:當然真實的輸入可能比給的示例複雜的多,但是不論多麼複雜,只需要使用拓撲排序排查一遍即可,這就是算法的魅力。實現起來也很簡單,大家可以將上面的C語言代碼稍加改動即可,這裡景禹給大家提供利用棧實現的Java代碼:
class Solution {
    public boolean canFinish(int numCourses, int[][] prerequisites) {
        int[] indegrees = new int[numCourses];
        List<List<Integer>> adjacency = new ArrayList<>();
        LinkedList<Integer> stack = new LinkedList<>();

        for(int i = 0; i < numCourses; i++)
            adjacency.add(new ArrayList<>());

        for(int[] cp : prerequisites) {
            indegrees[cp[0]]++;
            adjacency.get(cp[1]).add(cp[0]);
        }

        for(int i = 0; i < numCourses; i++)
            if(indegrees[i] == 0) stack.add(i);

        while(!stack.isEmpty()) {
            int pre = stack.pollLast();
            numCourses--;
            for(int cur : adjacency.get(pre))
                if(--indegrees[cur] == 0) stack.add(cur);
        }
        return numCourses == 0;
    }
}

還有一道題目,LeetCode 210. 課程表 II (course-schedule)),有興趣的小禹禹可以去試一試,其實只要在 207 的解法上增加一個輸出即可,景禹就不在這裡廢話了,但是景禹要給大家提供一種輸出所有拓撲序列的代碼,可供學有餘力的小禹禹抽空自己調試運行理解。
import java.util.*; 

class Graph { 
    int V; 

    List<Integer> adjListArray[]; 

    public Graph(int V) { 

    this.V = V; 

    @SuppressWarnings("unchecked") 
    List<Integer> adjListArray[] = new LinkedList[V]; 

    this.adjListArray = adjListArray; 

    for (int i = 0; i < V; i++) { 
       adjListArray[i] = new LinkedList<>(); 
    } 
 } 

 public void addEdge(int src, int dest) { 

    this.adjListArray[src].add(dest); 

 } 
 

private void allTopologicalSortsUtil(boolean[] visited, 
    int[] indegree, ArrayList<Integer> stack) { 
    boolean flag = false; 

    for (int i = 0; i < this.V; i++) { 
       if (!visited[i] && indegree[i] == 0) { 
        visited[i] = true; 
        stack.add(i); 
        for (int adjacent : this.adjListArray[i]) { 
       indegree[adjacent]--; 
    } 
    allTopologicalSortsUtil(visited, indegree, stack); 
    
    visited[i] = false; 
    stack.remove(stack.size() - 1); 
    for (int adjacent : this.adjListArray[i]) { 
       indegree[adjacent]++; 
    } 
    flag = true; 
    } 
  } 

    if (!flag) { 
       stack.forEach(i -> System.out.print(i + " ")); 
       System.out.println(); 
    } 
 } 
 

public void allTopologicalSorts() { 
    boolean[] visited = new boolean[this.V]; 

    int[] indegree = new int[this.V]; 

    for (int i = 0; i < this.V; i++) { 
       for (int var : this.adjListArray[i]) { 
          indegree[var]++; 
       } 
    } 

    ArrayList<Integer> stack = new ArrayList<>(); 

    allTopologicalSortsUtil(visited, indegree, stack); 
 } 
 

 public static void main(String[] args) { 

    Graph graph = new Graph(6); 
    graph.addEdge(5, 2); 
    graph.addEdge(5, 0); 
    graph.addEdge(4, 0); 
    graph.addEdge(4, 1); 
    graph.addEdge(2, 3); 
    graph.addEdge(3, 1); 

    System.out.println("All Topological sorts"); 
    graph.allTopologicalSorts(); 
 } 

總結 拓撲排序在實際的生產中很常見,最經典的例子就是課程表,當然還可以進行代碼的依賴關係處理等等,此外,拓撲排序還是之後要將的關鍵路徑的基礎,所以希望各位小禹禹好好學習,有所進步。

相關焦點

  • 一分鐘算法——拓撲排序(圖論)
    拓撲排序在了解本期拓撲排序的內容之前,你需要看過之前幾期的有關算法的文章
  • 雲計算開發實例:Python3 拓撲排序
    對一個有向無環圖(Directed Acyclic Graph簡稱DAG)G進行拓撲排序,是將G中所有頂點排成一個線性序列,使得圖中任意一對頂點u和v,若邊(u,v)∈E(G),則u在線性序列中出現在v之前。
  • 拓撲排序原理與解題套路
    點擊藍色「五分鐘學算法」關注我喲加個「星標」,一起學算法其實最開始學習算法,聽到拓撲排序這幾個字我也是懵逼的,後來學著學著才慢慢知道是怎麼一回事。拓撲排序解決的是依賴圖問題。依賴圖表示的是節點的關係是有依賴性的,比如你要做事件 A,前提是你已經做了事件 B。除了 「先有雞還是先有蛋」 這類問題,一般來說事件的依賴關係是單向的,因此我們都用有向無環圖來表示依賴關係。拓撲排序就是根據這些依賴來給出一個做事情,或者是事件的一個順序。
  • 圖解:「歸併排序」
    今天小浩給大家分享一篇關於歸併排序的文章。考察歸併排序的題目可以形態各異,但是萬變不離其宗,希望看完今日之章,你能掌握歸併排序及其思想大成。歸併排序 歸併排序和之前的冒泡排序、插入排序和選擇排序不同,其蘊含了一種分治的思想,在本文中我們會詳細說明。
  • 硬核科普:什麼是拓撲?
    「橡皮泥幾何」 入門我在大學學習拓撲時,總是不可避免地會遇到朋友和親戚們的提問:「拓撲到底是什麼?」這個問題很難回答,每次我都會給出略有不同的答案,但是答案總是不那麼令人滿意。如果你曾經在網上搜索過拓撲,你肯定會遇到將甜甜圈變成咖啡杯的動畫,同樣,我給出的答案也都與此相關:為什麼甜甜圈跟咖啡杯在拓撲結構上是一樣的,立方體和球體拓撲上也是一樣的。但是這樣的答案並不能真正解釋真實的拓撲是什麼,拓撲怎麼應用以及其真正的價值是什麼。
  • 科普:什麼是拓撲?什麼是相變?
    【什麼是拓撲?什麼是相變?】看不懂今年的#諾貝爾獎# 物理學獎?為什麼這些字每個字都知道,合起來就不認識了?先別急,諾獎官方推特做了一個簡單的介紹。要想知道什麼是「物質的拓撲相變和拓撲相」。你得先知道什麼是拓撲、什麼是相變。[拓撲]:拓撲學是數學的一個分支。
  • 拓撲學是什麼?歐拉、佩雷爾曼,研究拓撲學的都是什麼神級天才?
    拓撲學(topology)是研究幾何圖形或空間在連續改變形狀後還能保持不變的一些性質的學科。它只考慮物體間的位置關係而不考慮它們的形狀和大小。在拓撲學裡,重要的拓撲性質包括連通性與緊緻性。聽著是不是很難懂?
  • 總線型拓撲結構優缺點是什麼
    什麼是拓撲結構   計算機網絡拓撲結構是指網絡中各個站點相互連接的形式,在區域網中明確一點講就是文件伺服器、工作站和電纜等的連接形式。現在最主要的拓撲結構有總線型拓撲、星形拓撲、環形拓撲、樹形拓撲(由總線型演變而來)以及它們的混合型。
  • excel排序技巧:排序功能應用匯總
    小小的排序,也有很大的學問,之前經常遇到學員來問小編排序的問題,乾脆今天就給大家匯總一下,一次性搞明白~1、普通排序選中表格中任意單元格,點擊「數據」選項卡下的排序和篩選組中的「升序」、「降序」,即可直接完成排序。這時的排序結果是以選中單元格所在列,按漢語拼音首字母順序排列的。
  • 星形拓撲,環形拓撲網狀拓撲
    衛星星座拓撲結構是設計衛星網絡的物理結構,是衛星網絡建設首要解決的問題,因此下面對衛星網絡星座進行介紹關於衛星星座拓撲結構的探討是一個複雜的多學科範圍的課題,在本文中,僅對衛星星座拓撲結構的分類做一個簡單的說明。
  • Excel排序的潛規則
    上期小爬教了大家Excel的基本排序功能,今天就為大家分享一下排序的潛規則;相信很多小夥伴子工作的時候也經常遇到過類似的問題,那就是我們平時的排序都是按照列來進行排序,如果需要按行、按顏色排序又該如何操作呢?下面小爬就來為大家講解如何操作。
  • 「水晶頭接法」網線水晶頭接法圖解 網線接頭怎麼接
    【水晶頭接法】網線水晶頭接法圖解 網線接頭怎麼接摘要:您知道網線水晶頭怎麼接嗎?一般來說,網線水晶頭接法主要有兩種,一種是平行線接法,另一種是交叉線接法。首先要準備好壓線鉗,水晶頭,網線,網線測試儀。下面買購網小編給大家介紹網線水晶頭接法圖解,保證大家看過後就能學會。一般來說,網線水晶頭接法主要有兩種,一種是平行線接法,另一種是交叉線接法。下面具體來講解下這兩種接法的詳細步驟。
  • 具有自旋量子霍爾效應的拓撲材料,咖啡杯與甜甜圈拓撲等價?
    咖啡杯與甜甜圈可以在拓撲學上等價是因為甜甜圈和咖啡杯都只有一個洞,你服不服。拓撲材料的定義到底是什麼?霍爾效應扯上量子力學,這樣的拓撲材料是華人科學家發現的!今天和大家聊聊具有自旋量子霍爾效應的拓撲材料,在開始之前要先說說著名的華裔物理學家,史丹福大學教授張首晟,雖然這位老師已經離世,但是其學術貢獻依然造福著人類。
  • 拓撲絕緣體究竟是什麼東西?為什麼這麼受科學家青睞?
    Michael Kosterlitz)共同獲得了諾貝爾物理學獎,以表彰他們在理論上發現了物質的拓撲相變和拓撲相。那麼拓撲絕緣體究竟是什麼呢?我們一起來科普一下吧。什麼是拓撲絕緣體?按照導電性質的不同,材料可分為「導體」和「絕緣體」兩大類;而更進一步,根據電子態的拓撲性質的不同,「絕緣體」和「導體」還可以進行更細緻的劃分。拓撲絕緣體就是根據這樣的新標準而劃分的區別於其他普通絕緣體的一類絕緣體。
  • 超能課堂(196):決定電源性能的雙管正激和LLC諧振拓撲是什麼?
    那麼PC電源中各種拓撲又有些什麼差別呢?這就是一門學問了,涉及到的知識有些多,如果每一種拓撲都要談及,而且還是從電路理論講起,那麼以一篇超能課堂的篇幅可能連入門都算不上。為此今天我們挑選了目前主流的雙管正激和LLC諧振拓撲來進行簡單的講解,希望能夠起到拋磚引玉的作用,讓大家熱烈討論的同時也能進一步充實自己的知識。
  • excel中排序的使用方法
    這一章我們來了解下排序的使用方法,Excel中排序的使用方法非常的簡單,重點掌握自定義排序的使用方法首選來介紹下Excel中排序的規則排序的一般規則有3種根據筆劃順序進行排序會根據單元格文字筆畫的多少進行排序我們可以在Excel中的開始功能組中找到排序以及自定義排序升序排列:從小到大的進行排列,數據越來越大降序排列:從大到小的進行排序,數據越來越小很對時候我們選擇某一部分進行排序的話會跳出如下圖所示的對話框
  • 十二生肖是按照什麼排序的?兩個傳說,一個真相
    我們每個人都擁有自己的生肖,但你知道十二生肖是按照什麼排序的?關於十二生肖的排序,有兩個傳說,一個真相,讓我們來看看吧。第一個傳說,傳說在很久以前,玉帝昭告天下所有的動物,明天要選十二個動物來做生肖,哪十二隻動物最早到達殿上來報導,便把這十二隻動物定位生肖。所有的動物都非常的高興,個個摩拳擦掌,準備一大早便去報導。
  • 物理史上首份「拓撲圖鑑」,鋪平科學家尋找拓撲絕緣體之路
    上周,《Nature》刊登的一篇論文就為我們展示了一份意義深遠的「拓撲圖鑑」,從原理上揭示了哪些材料會具有拓撲效應——這將幫助科學家深刻探索馬約拉納費米子、外爾費米子等奇異粒子——在這點上,最近關於「天使粒子」的研究成果就是在超導拓撲材料上達成的。
  • 拓撲絕緣體簡介
    三位科學家戴維·索利斯、鄧肯·霍爾丹、麥可·科斯特利茨,因為「理論發現拓撲相變和拓撲相物質」獲獎。本刊在2012年發表了清華大學物理系呂衍鳳、陳曦、薛其坤的撰文《拓撲絕緣體簡介》,現整理出來以微信版(略去了參考文獻)再現讀者。
  • WLAN是什麼意思?WLAN的拓撲結構詳解
    本篇將介紹的是WLAN的拓撲結構知識的分享,有興趣的朋友可以了解一下!WLAN是什麼意思?WLAN是指無線區域網。下面仔細給大家講解下關於這方面的知識,WLAN有兩個主要類別,一個自我監管網絡(一個點對點網絡,通常稱為Ad-Hoc網絡)和一個網絡基礎設施(網絡基礎設施)。自調節WLAN是一種點對點模型網絡,旨在滿足臨時服務需求。