Kruskal算法和Prim算法都是用於查找無向帶權圖中的最小生成樹。但是Kruskal算法的時間復雜度為O(Elog2(E)),而Prim算法則是O(Elog2(n)),考慮到最復雜的情況|E|=|V|^2,這時候Kruskal算法的上限為O(2Elog2(V)),與Prim算法相差不大。並且Kruskal的核心算法為排序和並查集,相較於Prim算法需要實現最小堆和迪傑卡斯特算法,顯然要簡單很多。
對於圖G(V,E)中的最小生成樹U,我們可以證明E中權重最小的邊一定屬於U。假設(u,v)為權重最小的邊,且(u,v)不屬於U,則在U中存在一條從u到v的路徑u,x,...,v,顯然我們可以通過從U中移除(u,x)並加入(u,v)降低U的權重,這與U是最小生成樹的前提相悖,故命題成立。
在完成了上面一步之后,我們可以將u和v合並做一個結點,從而得到新的頂點集合V',同時從E中移除(u,v)得到新的邊集E',我們可以保證G(V',E')中的最小生成樹T滿足 T=U\{(u,v)}。可以反向說明,假設我們找到了G(V',E')的最小生成樹T,此時我們將某個結點分裂作u與v,並用(u,v)連通u和v,並要求新圖中的最小生成樹必須包含(u,v)。假設我們此時得到了新的最小生成樹PU{(u,v)},然而我們重新合並u與v之后,P將成為G(V',E')的一個生成樹,但是它不是最小的,故TU{(u,v)}的權重必定小於PU{(u,v)},因此T=U\{(u,v)}。利用遞歸的思路可以通過不斷查找最小邊來找到集合中的最小生成樹。
上面的過程中我們可以通過並查集來實現頂點的合並,並且利用任意O(nlog2(n))時間復雜度的排序算法對所有邊進行預先排序,之后按權重從小到大遍歷所有邊,如果邊的兩端未被合並入一個頂點(不在同一個並查集中),那么就將邊兩端的頂點合並,並將邊加入最小生成樹中。等到整個流程結束就可以得到最小生成樹,顯然合並最多發生|V|-1次,因此最小生成樹中包含|V|-1條邊。且由於我們取得的邊是優先取未被移除的低權邊,因此得到的每條邊都是正確的。時間復雜度決定於對邊的排序這一流程,為O(Elog2E)。
1 kruskal() 2 edgeList = empty-list 3 mst = empty-list 4 for edge in E 5 edgeList.add(edge) 6 sort(edgeList) 7 for edge in edgeList 8 if(edge.src.findSet() != edge.dst.findSet()) 9 union(edge.src, edge.dst) 10 mst.add(edge) 11 return mst