查找最小生成樹:普里姆算法算法(Prim)算法


一、算法介紹

  普里姆算法Prim's algorithm),圖論中的一種算法,可在加權連通圖里搜索最小生成樹。意即由此算法搜索到的子集所構成的中,不但包括了連通圖里的所有頂點,且其所有邊的權值之和亦為最小。像 Kruskal算法一樣,Prim算法也是貪婪算法

二、Prim算法思想

  Prim算法的思想很簡單,一棵生成樹意味着必須連接所有頂點。因此必須將兩個不相交的頂點子集連接起來才能生成生成樹 並且它們必須以最小的權重邊連接,以使其成為最小的生成樹(MST)它從一棵空的生成樹開始。這個想法是維持兩組頂點。第一組包含 MST 中已包含的頂點,另一組包含尚未包含的頂點。在每一步中,它都會考慮連接兩組的所有邊,並從這些邊中選取最小權重邊。選取完邊后,它將邊的另一個頂點點移動到包含 MST 的集合。

  將圖中的兩組頂點連接起來的一組邊線圖論稱為是表示圖的一組頂點中的兩個不相交的子集因此,在Prim算法的每一步中,都會去找到一個割線(分為兩組,一組包含MST中已經包含的頂點,另一組包含其余的頂點),從中選取最小權重邊,然后將此頂點包含到 MST 的集合中。

步驟:

1)創建一個集合 mstSet該集合跟蹤已包含在 MST 中的頂點。

2)為輸入圖中的所有頂點分配一個鍵值。將所有鍵值初始化為 INFINITE。將第一個頂點的鍵值指定為 0,以便首先選擇它。

3)雖然當前的 mstSet 不包括所有頂點:

  a)選擇一個 mstSet 中不存在且具有最小鍵值的頂點 u

  b) 包含到 mstSet 中。

  c)更新 u 的所有相鄰頂點的鍵值。要更新鍵值,需遍歷所有相鄰的頂點。對於每個相鄰頂點 v,如果邊 u-v 的權重小於 v 的先前鍵值,則將鍵值更新為 u-v 的權重。

  下面用一個例子來理解:

   集合 mstSet 最初為空,並且分配給頂點的鍵為 {0,INF,INF,INF,INF,INF,INF,INF},其中 INF 表示無窮大。現在選擇具有最小鍵值的頂點。選擇頂點0,將其包含在 mstSet 中。因此,mstSet 變為 {0}。在包含到 mstSet 之后,更新相鄰頂點的鍵值。0的相鄰頂點是 1 和 7。1 和 7 的關鍵值更新為 4 和 8。下面的子圖顯示了頂點及其關鍵值,僅顯示了具有有限關鍵值的頂點。MST 中包含的頂點顯示為綠色。

   選擇具有最小鍵值且尚未包含在 MST 中的頂點(不在 mstSet 中)。選擇頂點1並將其添加到 mstSet。因此,mstSet 現在變為 {0,1}。更新相鄰頂點1的鍵值,頂點2的鍵值變為 8。

 

 

 

   選擇具有最小鍵值且尚未包含在 MST 中的頂點(不在 mstSet 中)。我們可以選擇頂點7 或 頂點2,讓頂點7被選擇。因此,mstSet 現在變為 {0,1,7}。更新相鄰頂點7的關鍵值。頂點 6 和 8 的關鍵值變得有限(分別為 1 和 7)。

 

 

   選擇具有最小鍵值且尚未包含在 MST 中的頂點(不在 mstSet 中)。選擇了頂點6。這樣 mstSet 現在變成 {0,1,7,6}。更新相鄰頂點6的關鍵點值。更新頂點 5 和 8 的關鍵點值。

 

 

  重復上述步驟,直到 mstSet 包含給定圖的所有頂點。最后,我們得到下圖。

 

 

三、算法代碼

Prim算法:

 1     /**
 2      * 為使用鄰接矩陣表示的圖構造和打印MST
 3      *
 4      * @param graph 圖的鄰接矩陣
 5      */
 6     public void primMST(int[][] graph) {
 7         /* 存儲構造的MST */
 8         int[] parent = new int[V];
 9 
10         /* 用於選擇切割中最小權重的邊的關鍵值 */
11         int[] key = new int[V];
12 
13         /* 表示尚未包含在MST中的一組頂點 */
14         Boolean[] mstSet = new Boolean[V];
15 
16         /* 將所有鍵初始化為INFINITE */
17         for (int i = 0; i < V; i++) {
18             key[i] = Integer.MAX_VALUE;
19             mstSet[i] = false;
20         }
21 
22         /* 首先把一個頂點包含在MST中 */
23         key[0] = 0;     // 設置鍵0,以便此頂點被選為第一個頂點
24         parent[0] = -1; // 第一個節點始終是MST的根
25 
26         for (int count = 0; count < V-1; count++) {
27             /* 從頂點集合中選擇尚未包含在MST中最小關鍵點頂點 */
28             int u = minKey(key, mstSet);
29 
30             /* 將選取的頂點添加到MST集 */
31             mstSet[u] = true;
32 
33             // graph[u][v]僅對於m的相鄰頂點為非零
34             // 對於MST中尚未包含的頂點,mstSet[v]為false
35             // 僅當graph[u][v]小於key[v]時更新密鑰
36             for (int v = 0; v < V; v++) {
37                 if (graph[u][v] != 0 && !mstSet[v] && graph[u][v] < key[v]) {
38                     parent[v] = u;
39                     key[v] = graph[u][v];
40                 }
41             }
42         }
43 
44         /* 打印構造的MST */
45         printMST(parent, graph);
46     }

  選取尚未包含在 MST 的最小關鍵值的頂點。

 1     /**
 2      * 用於從尚未包含在MST中的一組頂點中查找具有最小關鍵值的頂點
 3      *
 4      * @param key
 5      * @param mstSet
 6      * @return
 7      */
 8     private int minKey(int[] key, Boolean[] mstSet) {
 9         /* 初始化最小值 */
10         int min = Integer.MAX_VALUE, min_index = -1;
11 
12         for (int v = 0; v < V; v++) {
13             if (!mstSet[v] && key[v] < min) {
14                 min = key[v];
15                 min_index = v;
16             }
17         }
18 
19         return min_index;
20     }

  使用鄰接矩陣的 Prim 算法中找到所有最小權邊共需時間復雜度O(V2)。使用簡單的二叉堆鄰接表來表示的話,算法的時間復雜度可減少至 O(E·log2V),其中 E 為圖的邊集,V 為圖的點集。

 

本文源代碼:

 

  1 package algorithm.mst;
  2 
  3 public class PrimAlgorithm {
  4     private static int V = 5;
  5 
  6     /**
  7      * 用於從尚未包含在MST中的一組頂點中查找具有最小關鍵值的頂點
  8      * O(V)
  9      *
 10      * @param key
 11      * @param mstSet
 12      * @return
 13      */
 14     private int minKey(int[] key, Boolean[] mstSet) {
 15         /* 初始化最小值 */
 16         int min = Integer.MAX_VALUE, min_index = -1;
 17 
 18         for (int v = 0; v < V; v++) {
 19             if (!mstSet[v] && key[v] < min) {
 20                 min = key[v];
 21                 min_index = v;
 22             }
 23         }
 24 
 25         return min_index;
 26     }
 27 
 28     /**
 29      * 打印構造的MST
 30      * @param parent
 31      * @param graph
 32      */
 33     private void printMST(int[] parent, int[][] graph) {
 34         System.out.println("Edge \t Weight");
 35         for (int i = 1; i < V; i++) {
 36             System.out.println(parent[i] + " - " + i + "\t" + graph[i][parent[i]]);
 37         }
 38     }
 39 
 40     /**
 41      * 為使用鄰接矩陣表示的圖構造和打印MST
 42      *
 43      * @param graph 圖的鄰接矩陣
 44      */
 45     public void primMST(int[][] graph) {
 46         /* 存儲構造的MST */
 47         int[] parent = new int[V];
 48 
 49         /* 用於選擇切割中最小權重的邊的關鍵值 */
 50         int[] key = new int[V];
 51 
 52         /* 表示尚未包含在MST中的一組頂點 */
 53         Boolean[] mstSet = new Boolean[V];
 54 
 55         /* 將所有鍵初始化為INFINITE */
 56         for (int i = 0; i < V; i++) {
 57             key[i] = Integer.MAX_VALUE;
 58             mstSet[i] = false;
 59         }
 60 
 61         /* 首先把一個頂點包含在MST中 */
 62         key[0] = 0;     // 設置鍵0,以便此頂點被選為第一個頂點
 63         parent[0] = -1; // 第一個節點始終是MST的根
 64 
 65         for (int count = 0; count < V-1; count++) {
 66             /* 從頂點集合中選擇尚未包含在MST中最小關鍵點頂點 */
 67             int u = minKey(key, mstSet);
 68 
 69             /* 將選取的頂點添加到MST集 */
 70             mstSet[u] = true;
 71 
 72             // graph[u][v]僅對於m的相鄰頂點為非零
 73             // 對於MST中尚未包含的頂點,mstSet[v]為false
 74             // 僅當graph[u][v]小於key[v]時更新密鑰
 75             for (int v = 0; v < V; v++) {
 76                 if (graph[u][v] != 0 && !mstSet[v] && graph[u][v] < key[v]) {
 77                     parent[v] = u;
 78                     key[v] = graph[u][v];
 79                 }
 80             }
 81         }
 82 
 83         /* 打印構造的MST */
 84         printMST(parent, graph);
 85     }
 86 
 87     public static void main(String[] args) {
 88         /* 創建一個圖的鄰接矩陣
 89                 2      3
 90             (0) -- (1) -- (2)
 91              |    /  \     |
 92             6|  8/    \5   |7
 93              |  /      \   |
 94             (3) --------- (4)
 95                     9
 96          */
 97         PrimAlgorithm t = new PrimAlgorithm();
 98         int[][] graph = new int[][] {
 99                 { 0, 2, 0, 6, 0 },
100                 { 2, 0, 3, 8, 5 },
101                 { 0, 3, 0, 0, 7 },
102                 { 6, 8, 0, 0, 9 },
103                 { 0, 5, 7, 9, 0 }
104         };
105 
106         // 打印解決方案
107         t.primMST(graph);
108     }
109 }
View Code


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM