在邊賦權圖中,權值總和最小的生成樹稱為最小生成樹。構造最小生成樹有兩種算法,分別是prim算法和kruskal算法。在邊賦權圖中,如下圖所示:
在上述賦權圖中,可以看到圖的頂點編號和頂點之間鄰接邊的權值,若要以上圖來構建最小生成樹。結果應該如下所示:
這樣構建的最小生成樹的權值總和最小,為17
在構建最小生成樹中,一般有兩種算法,prim算法和kruskal算法
在prim算法中,通過加入最小鄰接邊的方法來建立最小生成樹算法。首先構造一個零圖,在選一個初始頂點加入到新集合中,然后分別在原先的頂點集合中抽取一個頂點,使得構成的邊為權值最小,然后將該筆邊加入到圖中,並將抽出的頂點加入到新集合中,重復這個過程,知道新集合等於原先的集合。
代碼一:(java)

1 /** 2 * 最小生成樹的prim算法 3 * @author liuy 4 */ 5 public class Prim { 6 7 public static void prim(int num, float[][] weight) { //num為頂點數,weight為權 8 float[] lowcost = new float[num + 1]; //到新集合的最小權 9 10 int[] closest = new int[num + 1]; //代表與s集合相連的最小權邊的點 11 12 boolean[] s = new boolean[num + 1]; //s[i] == true代表i點在s集合中 13 14 s[1] = true; //將第一個點放入s集合 15 16 for(int i = 2; i <= num; i++) { //初始化輔助數組 17 lowcost[i] = weight[1][i]; 18 closest[i] = 1; 19 s[i] = false; 20 } 21 22 for(int i = 1; i < num; i++) { 23 float min = Float.MAX_VALUE; 24 int j = 1; 25 for(int k = 2; k <= num; k++) { 26 if((lowcost[k] < min) && (!s[k])) {//根據最小權加入新點 27 min = lowcost[k]; 28 j = k; 29 } 30 } 31 32 System.out.println("加入點" + j + ". " + j + "---" + closest[j]);//新加入點的j和與j相連的點 33 34 s[j] = true;//加入新點j 35 36 for(int k = 2; k <= num; k++) { 37 if((weight[j][k] < lowcost[k]) && !s[k]) {//根據新加入的點j,求得最小權 38 lowcost[k] = weight[j][k]; 39 closest[k] = j; 40 } 41 } 42 } 43 } 44 45 public static void main(String[] args) { 46 // ① 47 // / | / 48 // 6 1 5 49 // / | / 50 // ②-5--③--5--④ 51 // / // / 52 // 3 6 4 2 53 // // // 54 // ⑤--6-⑥ 55 //最小生成樹為: 56 // ① 57 // | 58 // 1 59 // | 60 // ②-5--③ ④ 61 // / / / 62 // 3 4 2 63 // / // 64 // ⑤ ⑥ 65 // 66 float m = Float.MAX_VALUE; 67 float[][] weight = {{0, 0, 0, 0, 0, 0, 0}, 68 {0, m, 6, 1, 5, m, m}, 69 {0, 6, m, 5, m, 3, m}, 70 {0, 1, 5, m, 5, 6, 4}, 71 {0, 5, m, 5, m, m, 2}, 72 {0, m, 3, 6, m, m, 6}, 73 {0, m, m, 4, 2, 6, m}};//上圖的矩陣 74 prim(weight.length - 1, weight); 75 //加入點3. 3---1 76 //加入點6. 6---3 77 //加入點4. 4---6 78 //加入點2. 2---3 79 //加入點5. 5---2 80 } 81 }
代碼二:(java)

1 package 最小生成樹; 2 /* 3 * 最小生成樹prim算法,加入最小鄰接邊生成最小生成樹。 4 * 首先構造一個零圖,選擇一個初始點加入到集合中, 5 * 然后分別從原來頂點的集合中抽取一個頂點, 6 * 選擇的標准是構造成的樹的權值最小, 7 * 循序漸進最終生成一棵最小生成樹 8 */ 9 public class prim { 10 11 /* 12 * m:定義為無法到達的距離 13 * weight:鄰接矩陣表,weight表示權值 14 * verNum:頂點的個數 15 * lowerW:到新集合的最小權值 16 * edge:存儲到新集合的邊 17 * checked:判定頂點是否被抽取的集合 18 */ 19 20 static int m=Integer.MAX_VALUE; 21 static int[][] weight={ 22 {0, 0, 0, 0, 0, 0}, 23 {0, m, 6, 9, 5, 13}, 24 {0, 6, m, 6,7,8}, 25 {0, 9,6,m,9,3}, 26 {0, 5,7,9,m,3}, 27 {0,13,8,3,3,m} 28 }; 29 static int verNum=weight.length; 30 static int []lowerW=new int[verNum]; 31 static int []edge=new int[verNum]; 32 static boolean []checked=new boolean[verNum]; 33 34 public void prim(int n,int [][]w){ 35 checked[1]=true; //抽取第一個頂點 36 37 for(int i=1;i<=n;i++){ //初始化頂點集合 38 lowerW[i]=w[1][i]; 39 edge[i]=1; 40 checked[i]=false; 41 } 42 43 for(int i=1;i<=n;i++){ 44 int min=Integer.MAX_VALUE; 45 int j=1; 46 for(int k=2;k<=n;k++){ //判定是否抽取該頂點 47 if(lowerW[k]<min&&(!checked[k])){ 48 min=lowerW[k]; 49 j=k; 50 } 51 } 52 if(i<n) //避免輸出第一個頂點到第一個頂點的情況 53 System.out.println(j+"-->"+edge[j]); 54 55 checked[j]=true; //將頂點加入到新集合中 56 57 for(int k=2;k<=n;k++){ //根據新加入的頂點,求得最小的權值 58 if((w[j][k]<lowerW[k])&&(!checked[k])){ 59 lowerW[k]=weight[j][k]; 60 edge[k]=j; 61 } 62 } 63 } 64 } 65 66 public static void main(String[] args) { 67 // TODO Auto-generated method stub 68 prim p=new prim(); 69 p.prim(verNum-1,weight); 70 } 71 }
在kruskal算法中,根據邊的權值以遞增的方式逐漸建立最小生成樹。具體操作是:將賦權圖每個頂點都看做森林,然后將圖中每條鄰接邊的權值按照升序的方式進行排列,接着從排列好的鄰接邊表中抽取權值最小的邊,寫入該邊的起始頂點和結束頂點,連接頂點將森林構成樹,然后讀取起始結束頂點的鄰接邊,優先抽取權值小的鄰接邊,繼續連接頂點將森林構成樹。添加鄰接邊的要求是加入到圖中的鄰接邊不構成回路。如此反復進行,直到已經添加n-1條邊為止。
代碼一:(java)

1 package 最小生成樹; 2 import java.util.ArrayList; 3 import java.util.Scanner; 4 /* 5 * 最小生成樹kruskal算法:首先將每個頂點作為一棵森林,升序比較該頂點的鄰接邊, 6 * 每次取最小權值的鄰接邊,將該鄰接邊連接的頂點與原先頂點構成一棵樹,接着尋找 7 * 下一個頂點,繼續按照鄰接邊權值升序進行比較,取權值最小的構成樹... 8 * 9 * 該類用一個Edge類構成一個鄰接邊的信息,包括鄰接邊的起始頂點與結束頂點,權值。 10 * 用類Edge創建對象,錄入對象信息,按照對象的權值進行比較,符合條件的對象加入 11 * 到鏈表中,最終按照鏈表順序輸出最小生成樹。 12 */ 13 public class kruskal { 14 15 /* 16 * Max:定義頂點數組的最大值 17 * edge:鏈表edge,存儲構造的Edge對象 18 * target:鏈表trget,存儲最終得到結果的Edge對象 19 * parent:存儲頂點信息的數組 20 * n:頂點數 21 */ 22 int Max=100; 23 ArrayList<Edge>edge=new ArrayList<Edge>(); 24 ArrayList<Edge>target=new ArrayList<Edge>(); 25 int[] parent=new int[Max]; 26 Float TheMax=Float.MAX_VALUE; 27 int n; 28 29 public void init(){ 30 /** 31 * p:起始頂點 32 * q:結束頂點 33 * w:邊的權值 34 * n:頂點個數 35 */ 36 Scanner scan =new Scanner(System.in); 37 int p,q; 38 double w; 39 System.out.println("請輸入結點的個數:"); 40 n=scan.nextInt(); 41 System.out.println("按照'A,B,C'的格式輸入邊與邊的信息,ABC分別代表邊的起始頂點,結束頂點,權值(輸入-1 -1 -1結束輸入):"); 42 while(true){ 43 p=scan.nextInt(); 44 q=scan.nextInt(); 45 w=scan.nextDouble(); 46 if(p<0||q<0||w<0)break; 47 Edge e=new Edge(); 48 e.start=p; 49 e.end=q; 50 e.weight=w; 51 edge.add(e); 52 } 53 for(int i=1;i<=n;++i){ //初始化邊的信息數組 54 parent[i]=i; 55 } 56 } 57 58 /* 59 * 對象合並,將上一對象的結束邊作為下一對象的起始邊 60 */ 61 public void union(int j,int k){ 62 for(int i=1;i<=n;++i){ 63 if(parent[i]==j) 64 parent[i]=k; 65 } 66 } 67 68 public void kruskal(){ 69 int i=0; //頂點 70 while(i<n-1&&edge.size()>0){ //如果只有一條邊或者沒有邊跳出 71 double min=Double.MAX_VALUE; 72 Edge temp=null; 73 for(int j=0;j<edge.size();++j){ //遍歷圖形 74 Edge tt=edge.get(j); 75 if(tt.weight<min){ //若兩個頂點有權值,即相連 76 min=tt.weight; 77 temp=tt; 78 } 79 } 80 81 //構造一棵樹 82 int jj=parent[temp.start]; 83 int kk=parent[temp.end]; 84 85 86 if(jj!=kk){ 87 ++i; //以end作為下一條邊的start,尋找下一條邊 88 target.add(temp); //將找到的邊放入目標集合中 89 union(jj,kk); 90 } 91 edge.remove(temp); //將臨時邊刪除 92 } 93 System.out.println("最小生成樹的路徑是:"); 94 for(int k=0;k<target.size();++k){ //輸出最小生成樹 95 Edge e=target.get(k); 96 System.out.println(e.start+"-->"+e.end); 97 } 98 } 99 100 public static void main(String[] args) { 101 // TODO Auto-generated method stub 102 kruskal kr=new kruskal(); 103 kr.init(); 104 kr.kruskal(); 105 } 106 } 107 /* 108 * start:起始頂點 109 * end:結束頂點 110 * weight:權值 111 */ 112 class Edge{ 113 public int start; 114 public int end; 115 public double weight; 116 }