算法描述
克魯斯卡爾算法是一種貪心算法,因為它每一步都挑選當前最輕的邊而並不知道全局路徑的情況.
算法最關鍵的一個步驟是要判斷要加入mst的頂點是否會形成回路,我們可以利用並查集的技術來做。
並查集的具體實現可參考:快速並查集
下面是對算法的一個簡單描述:
這是一個非常簡單易懂的算法,它面向邊而不是頂點,所以在算法開始的時候,它要先找出所有的crossing edges,而為了高效的找到最輕邊,用一個優先隊列來維護這些crossing edges.
/** * 找出所有crossing edges並加入優先隊列 */
private void findAllCrossingEdges(){
for(Vertex v:this.vertices) {
for(Edge edge:v.Adj) {
WeightedEdge we = (WeightedEdge)edge;
this.crossingEdges.add(we);
}
}
}
private PriorityQueue<WeightedEdge> crossingEdges = new PriorityQueue<WeightedEdge>();
算法實現
下面是克魯斯卡爾算法的一個實現:
/** * 克魯斯卡爾算法求MST * * 克魯斯爾卡算法也是一種貪心算法(greedy algorithm) * 1.總是挑選最輕的邊,如果這條邊的兩個端點沒有形成回路,就將這條邊加入MST * 2.在剩下的邊中,重復1. * */
public void kruskalMST() {
resetMemo();
//找出所有crossing edges
findAllCrossingEdges();
//初始化並查集
FastUnionFind uf = new FastUnionFind(vertexCount());
//算法用貪心策略,每一步都挑選最輕的邊來加入mst
//需要注意的是,在加入mst之前要考察邊的兩端頂點是否形成環路
while (!this.crossingEdges.isEmpty()) {
//最輕邊
WeightedEdge edge = crossingEdges.poll();
//如果點src和點to沒有形成環
if(!uf.isConnected(edge.src,edge.to)){
//將src和to連通
uf.union(edge.src,edge.to);
//將最輕邊加入mst
mst.offer(edge);
//更新mst的權重
mstWeight += edge.weight;
}
}
}
時間復雜度
算法需要對所有邊E 進行訪問,這步操作耗時O(E )
將邊入隊和出隊的操作耗時O(logE).
由於假設圖是連通的,並查判斷回路操作耗時O(logE)
所以整體耗時O(ElogE ).
由於 |E| < V*V,所以logE = 2logV,則可將算法復雜度寫為O(ElogV).