最小生成樹——Prim And Kruskal實現最小生成樹

什麼是最小生成樹?

一個有 n 個結點的連通圖的生成樹是原圖的極小連通子圖,且包含原圖中的全部 n 個結點,而且有保持圖連通的最少的邊。最小生成樹能夠用kruskal(克魯斯卡爾)算法或prim(普里姆)算法求出。算法

 

MST性質:

假設 N = (V, {E})是一個連通網,U是頂點集V的一個非空子集。若(u,v)是一條具備最小權值(代價)的邊,其中 u ∈ U, v ∈ V - U,則必存在一棵包含邊(u, v)的最小生成樹。數組

證實以下(反證法):ui

  假設連通網N的任何一棵最小生成樹中都不包含邊(u,v)。spa

  設T是N的一棵最小生成樹,則由上述假設可知,T中不包含(u,v)。因爲T是連通的,所以有一條從u到v的路徑。將T中的頂點集分爲兩部分:頂點集U和頂點集V - U,其中,頂點集U和相關的邊構成一棵最小生成樹T1,頂點集V - U和相關的邊也構成一棵最小生成樹T2,而且T1與T2之間只能有一條邊,不妨設爲(u`, v`),則u 和 u`之間、v和v`之間均是連通的。當把邊(u,v)加入最小生成樹T時,T中必有一條包含邊(u,v)的迴路,刪除邊(u`, v`),上述迴路即被刪除,由此獲得另外一棵生成樹T`,如圖所示。由於邊(u,v)的權值小於等於(u`,v`)的權值,則T`的代價也就小於等於T的代價,所以T`也是N的最小生成樹,且包含邊(u,v),與假設矛盾,得證。code

 

 什麼是Prim算法?

普里姆算法(Prim算法),圖論中的一種算法,可在加權連通圖裏搜索最小生成樹。意即由此算法搜索到的邊子集所構成的樹中,不但包括了連通圖裏的全部頂點(英語:Vertex (graph theory)),且其全部邊的權值之和亦爲最小。該算法於1930年由捷克數學家沃伊捷赫·亞爾尼克(英語:Vojtěch Jarník)發現;並在1957年由美國計算機科學家羅伯特·普里姆(英語:Robert C. Prim)獨立發現;1959年,艾茲格·迪科斯徹再次發現了該算法。所以,在某些場合,普里姆算法又被稱爲DJP算法、亞爾尼克算法或普里姆-亞爾尼克算法。blog

 

Prrim算法的基本思想是:假設N = (V, E)是連通網,令T = (U, TE)是N的最小生成樹。算法的基本思想是:T的初始狀態爲U = {Vo} (Vo∈V),TE = {},重複執行下述操做:數學

① 在全部u∈U,v∈V - U的邊(u,v)∈ E中,找一條代價最小的邊(ui, vj)併入集合TE,同時將 vj 併入 U 中;class

② 如此不斷重複,知道 U = V 爲止。此時 TE 必有 n - 1 條邊,則 T(V,TE)爲 N 的最小生成樹。計算機科學

 

連通圖:

 

最小生成樹:

Prim算法代碼實現:

 1 public class MST {
 2 
 3     /**
 4      * 這裏用鄰接矩陣來存儲圖
 5      * 頂點編號從 A 開始,總共有7個頂點
 6      * 這裏須要聲明一下,若是兩個頂點沒有邊直接鏈接,那麼就設置這連個頂點的權值(也可認爲是距離,這裏具體問題具體分析)無窮大
 7      */
 8     private static int[][] closeMatrix = {
 9             {0, 50, 60, Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE},
10             {50, 0, Integer.MAX_VALUE, Integer.MAX_VALUE, 40, Integer.MAX_VALUE, Integer.MAX_VALUE},
11             {60, Integer.MAX_VALUE, 0, 52, Integer.MAX_VALUE, Integer.MAX_VALUE, 45},
12             {Integer.MAX_VALUE, Integer.MAX_VALUE, 52, 0, 50, 30, 42},
13             {Integer.MAX_VALUE, 40, Integer.MAX_VALUE, 50, 0, 70, Integer.MAX_VALUE},
14             {Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE, 30, 70, 0, Integer.MAX_VALUE},
15             {Integer.MAX_VALUE, Integer.MAX_VALUE, 45, 42, Integer.MAX_VALUE, Integer.MAX_VALUE, 0},
16     };
17 
18     private static char[] vex = {'A', 'B', 'C', 'D', 'E', 'F', 'G'};
19 
20     /**
21      * @param closeMatrix
22      * @param startVex 這裏開始的頂點用數字表示,A-0 B-1 C-2 D-3 E-4 F-5 G-6
23      */
24     public static void prim(int[][] closeMatrix, int startVex) {
25         // 兩個輔助數組,由於隨着點加入U中,可能原先是無窮大,後面直接變成最小都有可能,因此下面的兩個數組的值都是在不停的變化
26         // lowCosts數組還有一個做用,若是lowCosts[i] == 0 表示編號爲i的頂點已經加入到U集合中了,反之則沒有加入U集合
27         // 這裏是爲想不改變原矩陣,因此纔開了lowCosts做爲輔助數組
28         int[] lowCosts = new int[vex.length];
29         // closest數組是用來存儲當前加入U集合中的頂點編號,做用:爲了後面打印邊時,能知道邊左邊的頂點編號
30         int[] closest = new int[vex.length];
31         // 一開始加入第一個頂點時,初始化以上兩個數組
32         // 加入一個頂點時沒有邊,因此不須要打印
33         for (int i = 0; i < vex.length; i++) {
34             lowCosts[i] = closeMatrix[startVex][i];
35             closest[i] = startVex;
36         }
37 
38         // k用於存儲當前離U集合最近的頂點編號
39         int k = startVex;
40         // 開始加入V - U集合中的頂點,循環n - 1遍,該循環的目的:找出n - 1個頂點
41         for (int i = 1; i < vex.length; i++) {
42             // 找出離最小權值的邊
43             int min = Integer.MAX_VALUE;
44             // 在V - U集合中找出離U最近的頂點k
45             for (int j = 0; j < vex.length; j++) {
46                 if (lowCosts[j] != 0 && lowCosts[j] < min) {
47                     // 更新
48                     min = lowCosts[j];
49                     k = j;
50                 }
51             }
52             // 程序運行到這,表示此時已經找到了咱們想要找到的離U集合最近的頂點了,也能夠將這裏最近理解爲代價最小或權值最小
53             // 打印邊
54             System.out.printf("邊(%c, %c)權爲:%d\n", vex[closest[k]], vex[k], min);
55             // 不要忘了,標誌編號爲k的頂點已經加入U集合
56             lowCosts[k] = 0;
57             // 修改lowCosts 和 closest 數組
58             // 修改仍在V - U集合中的頂點,若是已經在U集合中的頂點不須要修改
59             for (int j = 0; j < vex.length; j++) {
60                 // 如今要比的是編號爲k的頂點,全部頂點以編號爲k的頂點爲參考點,若是此時到它的權值比以前的到上一個頂點時的更優,則更新
61                 if (lowCosts[j] != 0 && closeMatrix[k][j] < lowCosts[j]) {
62                     // 此時 lowCosts[j] 的值再也不是一開始到開始第一加入的頂點時的權值了,而是到編號爲k的頂點的權值
63                     // 由於此時已經將k加入U集合,全部在 V - U集合中的頂點選取最小權值時都要以編號爲k的頂點做爲基點展開
64                     lowCosts[j] = closeMatrix[k][j];
65                     // 更新k,這一行相當重要
66                     closest[j] = k;
67                 }
68             }
69         }
70     }
71 
72     public static void main(String[] args) {
73         prim(closeMatrix, 0);
74     }
75 }

運行結果:搜索

相關文章
相關標籤/搜索