1. 程式人生 > >Python多重繼承排序原理(MRO演算法解析,拓撲排序,C3演算法)

Python多重繼承排序原理(MRO演算法解析,拓撲排序,C3演算法)

Python內建屬性__MRO__演算法解析

什麼是MRO

MRO(Method Resolution Order):方法解析順序。
Python語言包含了很多優秀的特性,其中多重繼承就是其中之一,但是多重繼承會引發很多問題,比如二義性,Python中一切皆引用,這使得他不會像C++一樣使用虛基類處理基類物件重複的問題,但是如果父類存在同名函式的時候還是會產生二義性,Python中處理這種問題的方法就是MRO。

對於MRO的歷史問題這個帖子說的非常詳細,但是後面說到C3演算法的時候說錯了。從評論中可以看出來。
python內建屬性MRO請不要看這個地方。
請不要看這個地方
這文章有點瑕疵,因為按照他的思路這個地方就不對了。

研究了一晚上才梳理了所有細節。聽我慢慢道來。
在箭頭之前的你都可以看一下,寫的非常好。等你看到這個的時候,你需要了解什麼是拓撲排序

拓撲排序

瞭解這一部分的可以跳到下面去。
重點:一個圖的拓撲排序是可以有多個的!!!!
經典實現演算法:

  1. kahn演算法
  2. 基於DFS的拓撲排序

很多情況下,拓撲排序問題往往會出現在一些中等複雜程度的計算系統中。這方面最典型的例子莫過於軟體安裝了,現在大多數作業系統都至少會有一個自動安裝軟體元件的系統(Ubuntu Linux 系統中的 apt-get,CentOS Linux 系統中的 RPM,Mac OS X 系統中的 brew 等),這些系統會自動檢測依賴關係中缺少的部分,並下載安裝它們,對於這一類工作,相關元件就必須按照一定的拓撲順序來安裝。

Kahn演算法:

摘一段維基百科上關於Kahn演算法的偽碼描述:

L← Empty list that will contain the sorted elements
S ← Set of all nodes with no incoming edges
while S is non-empty do
    remove a node n from S
    insert n into L
    foreach node m with an edge e from nto m do
        remove edge e from thegraph
        ifm has no other incoming edges then
            insert m into S
if graph has edges then
    return error (graph has at least onecycle)
else 
    return L (a topologically sortedorder)

思想很簡單,首先用鄰接表來構建一個圖。然後用一個臨界表來記錄每個頂點的入度,再用一個表來加入入度為0的點。最後一次輸出。(什麼是入度?a入度為0代表沒有點能指向a,為1就是又一個點能指向a)

方法如下:

  1. 首先初始化一個表存放著這個圖入度為0的點S。
  2. 從這個表裡面取資料(如果這個表是棧,就是先進後出的取,如果是佇列,就是先進先取。從這可以看出為什麼拓撲排序為什麼不唯一。),將該頂點放入List中。
  3. 通過迴圈遍歷由上面去出的頂點引出的所有邊,同時獲取該邊的另外一個頂點,如果改頂點的入度在減去本條邊之後為0,那麼就把這個頂點放到S的集合中,然後繼續重複2的動作。
  4. 當集合為空之後,檢查圖中是否還存在任何邊,如果存在的話,說明圖中存在環路。不存在,返回結果List,此List中的順序就是拓撲排序的結果。

需要程式碼實現的盆友看這裡:
拓撲排序程式碼實現java
參看連結如下:
參看連結

我總覺得按照連結中樓主的思路,紅色的地方是有錯誤的。誰能私聊我討論討論。

複雜度分析:

初始化入度為0的集合需要遍歷整張圖,檢查每個節點和每條邊,因此複雜度為O(E+V);

然後對該集合進行操作,又需要遍歷整張圖中的,每條邊,複雜度也為O(E+V);

因此Kahn演算法的複雜度即為O(E+V)。

C3演算法

既然弄懂來拓撲排序,那麼我們來看看C3演算法到底如何工作的。
官網解釋連結,英語還行的可以看看
快速檢視,看看這個對官網的翻譯連結


箭頭是核心,懂這一塊,應該就沒有問題來。