1. 程式人生 > 其它 >啟動優化 - 有向無環圖

啟動優化 - 有向無環圖

前言

說到 Android 啟動優化,大家第一時間可能會想到非同步載入。將耗時任務放到子執行緒載入,等到所有載入任務載入完成之後,再進入首頁。

多執行緒非同步載入方案確實是 ok 的。但如果遇到前後依賴的關係呢。比如任務2 依賴於任務 1,這時候要怎麼解決呢。

最簡單的方案是將任務1 丟到主執行緒載入,然後再啟動多執行緒非同步載入。

如果遇到更復雜的依賴呢。

任務3 依賴於任務 2, 任務 2 依賴於任務 1 呢,這時候你要怎麼解決。更復雜的依賴關係呢

總不能將任務 2,任務 3 都放到主執行緒載入吧,這樣多執行緒載入的意義就不大了。

有沒有更好的方案呢?

答案肯定是有的,使用有向無環圖。它可以完美解決先後依賴關係。

重要概念

有向無環圖(Directed Acyclic Graph, DAG)是有向圖的一種,字面意思的理解就是圖中沒有環。常常被用來表示事件之間的驅動依賴關係,管理任務之間的排程。

頂點:圖中的一個點,比如頂點 1,頂點 2。

邊:連線兩個頂點的線段叫做邊,edge。

入度:代表當前有多少邊指向它。

在上圖中,頂掉 1 的入度是 0,因為沒有任何邊指向它。頂掉 2 的入度是 1, 因為 頂掉 1 指向 頂掉 2. 同理可得出 5 的入度是 2,因為頂掉 4 和頂點 3 指向它

拓撲排序:拓撲排序是對一個有向圖構造拓撲序列的過程。它具有如下特點。

  • 每個頂點出現且只出現一次。
  • 若存在一條從頂點 A 到頂點 B 的路徑,那麼在序列中頂點 A 出現在頂點 B 的前面

由於有這個特點,因此常常用有向無環圖的資料結構用來解決依賴關係。

上圖中,拓撲排序之後,任務2肯定排在任務1之後,因為任務2依賴 任務1, 任務3肯定在任務2之後,因為任務3依賴任務2。

拓撲排序一般有兩種演算法,第一種是入度表法,第二種是 DFS 方法。下面,讓我們一起來看一下怎麼實現它。

入度表法

入度表法是根據頂點的入度來判斷是否有依賴關係的。若頂點的入度不為 0,則表示它有前置依賴。它也常常被稱作 BFS 演算法

演算法思想

  • 建立入度表,入度為 0 的節點先入隊
  • 當佇列不為空,進行迴圈判斷
  • 節點出隊,新增到結果 list 當中
  • 將該節點的鄰居入度減 1
  • 若鄰居節點入度為 0,加入佇列
  • 若結果 list 與所有節點數量相等,則證明不存在環。否則,存在環

例項講解

下圖所示的有向無環圖,採用入度表的方法獲取拓撲排序過程。

首先,我們選擇入度為 0 的頂點,這裡頂點 1 的入度為 0,刪除頂點 1 之後,圖變成如下。

這時候,頂點 2 和頂點 4 的入度都為 0,我們可以隨便刪除一個頂點。(這也就是為什麼圖的拓撲排序不是唯一的原因)。這裡我們刪除頂點 2,圖變成如下:

這時候,我們再刪除頂點 4,圖變成如下:

選擇入度為 0 的頂點 3,刪除頂點 3 之後,圖示稱如下,

最後剩餘頂點5,輸出頂點5,拓撲排序過程結束。最終的輸出結果為:

到此,優先無環圖的入度法的流程已經講解完畢。你清楚了嘛。

程式碼的話,下期會一起給出。

時間複雜度

設 AOE 網有 n 個事件,e 個活動,則演算法的主要執行是:

  • 求每個事件的ve值和vl值:時間複雜度是O(n+e) ;
  • 根據ve值和vl值找關鍵活動:時間複雜度是O(n+e) ;

因此,整個演算法的時間複雜度是O(n+e)

DFS 演算法

從上面的入度表法,我們可以知道,要得到有向無環圖的拓撲排序,我們的關鍵點要找到入度為 0 的頂點。然後接著刪除該結點的相鄰所有邊。再遍歷所有結點。直到入度為 0 的佇列為空。這種方法其實是 BFS。

說到 BFS,我們第一時間就想到 DFS。與 BFS 不同的是,DFS 的關鍵點在於找到,出度為0的頂點。

總結如下,深度優先搜尋過程中,當到達出度為0的頂點時,需要進行回退。在執行回退時記錄出度為0的頂點,將其入棧。則最終出棧順序的逆序即為拓撲排序序列。

演算法思想

  • 對圖執行深度優先搜尋。
  • 在執行深度優先搜尋時,若某個頂點不能繼續前進,即頂點的出度為0,則將此頂點入棧。
  • 最後得到棧中順序的逆序即為拓撲排序順序。

例項講解

同樣,以下圖講解 DFS 演算法的過程。

(1) 從頂點 1 開始出發,開始執行深度優先搜尋。順序為1->2->3->5。

(2)深度優先搜尋到達頂點5時,頂點5出度為0。將頂點5入棧。

(3)深度優先搜尋執行回退,回退至頂點3。此時頂點3的出度為0,將頂點3入棧。

(4)回退至頂點2,頂點2出度為0,頂點2入棧。

(5)回退至頂點1,頂點1可以前進位置為頂點4,順序為1->4。

(6)頂點4出度為0,頂點4入棧。

(7)回退至頂點1,頂點1出度為0,頂點1入棧。

(8)棧的逆序為1->4->2->3->5。此順序為拓撲排序結果。

時間複雜度

時間複雜度分析:首先深度優先搜尋的時間複雜度為O(V+E),而每次只需將完成訪問的頂點存入陣列中,需要O(1),因而總複雜度為O(V+E)。

小結

有向無環圖的拓撲排序其實並不難,難度中等。通常,我們一般使用 BFS 演算法來解決,DFS 演算法比較少用。

對於 BFS(入度表法),它的核心思想是

  1. 選擇一個沒有輸入邊(入度為0)的源頂點(若有多個則任選一個),
  2. 將它和它的輸出邊刪除。重複源頂點的刪除操作,直到不存在入度為0的源頂點為止。
  3. 最終,檢測圖中的頂點個數,若還有頂點存在則演算法無解,否則頂點的刪除順序就是拓撲排序的輸出順序。

https://github.com/gdutxiaoxu/AnchorTask

Android高階開發系統進階筆記、最新面試複習筆記PDF,我的GitHub

文末

您的點贊收藏就是對我最大的鼓勵!
歡迎關注我,分享Android乾貨,交流Android技術。
對文章有何見解,或者有何技術問題,歡迎在評論區一起留言討論!