最小生成樹 Prim演算法
阿新 • • 發佈:2019-01-23
一個無向帶權圖G=(V,E),其中n個頂點Vertex,以及連線各個頂點之間的邊Edge,可能有些頂點之間沒有邊,每條邊上的權值都是非負值。
生成樹:
G的一個子圖,包含了所有的Vertex,和部分的Edge。
最小生成樹:
所有的生成樹中,各條Edge上的權值總和最小的一個。
例子:設計通訊網路時,各個城市之間鋪設線路,最經濟的方案。
最小生成樹性質:
G=(V,E),
S是V的真子集,
如果u在S中,v在V-S中,且(u,v)是圖的一條邊,稱之為特殊邊,且(u,v)是所有特殊邊中最短的,
那麼,(u,v)這條邊一定在最小生成樹中。
Prim演算法:
任意指定一個頂點作為起始點,放在S中。
每一步將最短的特殊邊放入S中,需要n-1步,即可把所有的其他的點放入S中。演算法結束。
對於這個圖,Prim演算法的過程為:
程式碼實現如下:
/** * 最小生成樹 minimum spanning tree * @author xuefeng * */ public class MST { public static final int NOT_REACHED = -1; /** * @param E n*n */ public static void prim(int[][] E) { int n = E.length; boolean[] S = new boolean[n]; int[] dist = new int[n]; int[] prev = new int[n]; S[0] = true; // 選取頂點1作為起始點 for (int i = 1; i < n; i++) { dist[i] = E[0][i]; prev[i] = 0; S[i] = false; } int min = -1, minV = -1; for (int i = 1; i < n; i++) { min = -1; minV = -1; // 選擇離S中頂點最近的點 for (int j = 1; j < n; j++) { if (!S[j] && dist[j] != -1 && (min == -1 || dist[j] < min)) { min = dist[j]; minV = j; } } if (minV == -1) continue; S[minV] = true; // S中多了個點,需要改變S離外面的點的最短距離 for (int j = 1; j < n; j++) { if (!S[j] && isReachable(E, minV, j) && (dist[j] == -1 || E[minV][j] < dist[j])) { dist[j] = E[minV][j]; prev[j] = minV; } } // 輸出測試結果 for (int j = 0; j < n; j++) { if (S[j]) { System.out.print((j + 1) + " "); } } System.out.println(); } } private static boolean isReachable(int[][] E, int v1, int v2) { return E[v1][v2] != NOT_REACHED; } public static void main(String[] args) { int[][] E = { { -1, 6, 1, 5, -1, -1 }, { 6, -1, 5, -1, 3, -1 }, { 1, 5, -1, 5, 6, 4 }, { 5, -1, 5, -1, -1, 2 }, { -1, 3, 6, -1, -1, 6 }, { -1, -1, 4, 2, 6, -1 } }; prim(E); } }
輸出為:
1 3
1 3 6
1 3 4 6
1 2 3 4 6
1 2 3 4 5 6
可由prev陣列構造出最小生成樹。
演算法複雜度為O(n^2)