java實現最小生成樹的prim算法和kruskal算法


 

在邊賦權圖中,權值總和最小的生成樹稱為最小生成樹。構造最小生成樹有兩種算法,分別是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 }
View Code

代碼二:(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 }
View Code

在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 }
View Code

 


免責聲明!

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



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