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

jopen 9年前發布 | 143K 次閱讀 算法

在邊賦權圖中,權值總和最小的生成樹稱為最小生成樹。構造最小生成樹有兩種算法,分別是prim算法和kruskal算法。在邊賦權圖中,如下圖所示:

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

在上述賦權圖中,可以看到圖的頂點編號和頂點之間鄰接邊的權值,若要以上圖來構建最小生成樹。結果應該如下所示:

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

這樣構建的最小生成樹的權值總和最小,為17


在構建最小生成樹中,一般有兩種算法,prim算法和kruskal算法

在prim算法中,通過加入最小鄰接邊的方法來建立最小生成樹算法。首先構造一個零圖,在選一個初始頂點加入到新集合中,然后分別在原先的頂點集合中抽取一個頂點,使得構成的邊為權值最小,然后將該筆邊加入到圖中,并將抽出的頂點加入到新集合中,重復這個過程,知道新集合等于原先的集合。

代碼如下:

package 最小生成樹;
/*
 * 最小生成樹prim算法,加入最小鄰接邊生成最小生成樹。
 * 首先構造一個零圖,選擇一個初始點加入到集合中,
 * 然后分別從原來頂點的集合中抽取一個頂點,
 * 選擇的標準是構造成的樹的權值最小,
 * 循序漸進最終生成一棵最小生成樹
 */
public class prim {
 /**
  * @author 劉雁冰
  * @date 2015-02-13 20:23
  */
 
 /*
  * m:定義為無法到達的距離
  * weight:鄰接矩陣表,weight表示權值
  * verNum:頂點的個數
  * lowerW:到新集合的最小權值
  * edge:存儲到新集合的邊
  * checked:判定頂點是否被抽取的集合
  */
 
 static int m=Integer.MAX_VALUE;
 static int[][] weight={
   {0, 0, 0, 0, 0, 0},  
   {0, m, 6, 9, 5, 13},  
   {0, 6, m, 6,7,8},  
   {0, 9,6,m,9,3},  
   {0, 5,7,9,m,3},  
   {0,13,8,3,3,m}  
 };
 static int verNum=weight.length;
 static int []lowerW=new int[verNum];
 static int []edge=new int[verNum];
 static boolean []checked=new boolean[verNum];
 
 public void prim(int n,int [][]w){
  checked[1]=true;            //抽取第一個頂點
  
  for(int i=1;i<=n;i++){          //初始化頂點集合
   lowerW[i]=w[1][i];
   edge[i]=1;
   checked[i]=false;
  }
  
  for(int i=1;i<=n;i++){
   int min=Integer.MAX_VALUE;
   int j=1;
   for(int k=2;k<=n;k++){        //判定是否抽取該頂點
    if(lowerW[k]<min&&(!checked[k])){
     min=lowerW[k];
     j=k;
    }
   }
   if(i<n)                //避免輸出第一個頂點到第一個頂點的情況
   System.out.println(j+"-->"+edge[j]);
   
   checked[j]=true;           //將頂點加入到新集合中
   
   for(int k=2;k<=n;k++){        //根據新加入的頂點,求得最小的權值
    if((w[j][k]<lowerW[k])&&(!checked[k])){
     lowerW[k]=weight[j][k];
     edge[k]=j;
    }
   }
  }
 }
 
 public static void main(String[] args) {
  // TODO Auto-generated method stub
  prim p=new prim();
  p.prim(verNum-1,weight);
 }
}


測試結果如下:

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

在kruskal算法中,根據邊的權值以遞增的方式逐漸建立最小生成樹。具體操作是:將賦權圖每個頂點都看做森林,然后將圖中每條鄰接邊的權值按照升序的方式進行排列,接著從排列好的鄰接邊表中抽取權值最小的邊,寫入該邊的起始頂點和結束頂點,連接頂點將森林構成樹,然后讀取起始結束頂點的鄰接邊,優先抽取權值小的鄰接邊,繼續連接頂點將森林構成樹。添加鄰接邊的要求是加入到圖中的鄰接邊不構成回路。如此反復進行,直到已經添加n-1條邊為止。

代碼如下:

package 最小生成樹;
import java.util.ArrayList;
import java.util.Scanner;
/*
 * 最小生成樹kruskal算法:首先將每個頂點作為一棵森林,升序比較該頂點的鄰接邊,
 * 每次取最小權值的鄰接邊,將該鄰接邊連接的頂點與原先頂點構成一棵樹,接著尋找
 * 下一個頂點,繼續按照鄰接邊權值升序進行比較,取權值最小的構成樹...
 * 
 * 該類用一個Edge類構成一個鄰接邊的信息,包括鄰接邊的起始頂點與結束頂點,權值。
 * 用類Edge創建對象,錄入對象信息,按照對象的權值進行比較,符合條件的對象加入
 * 到鏈表中,最終按照鏈表順序輸出最小生成樹。
 */
public class kruskal {
 /**
  * @author 劉雁冰
  * @date 2015-02-13 20:23
  */
 
 /*
  * Max:定義頂點數組的最大值
  * edge:鏈表edge,存儲構造的Edge對象
  * target:鏈表trget,存儲最終得到結果的Edge對象
  * parent:存儲頂點信息的數組
  * n:頂點數
  */
 int Max=100;
 ArrayList<Edge>edge=new ArrayList<Edge>();
 ArrayList<Edge>target=new ArrayList<Edge>();
 int[] parent=new int[Max];
 Float TheMax=Float.MAX_VALUE;
 int n;
 
 public void init(){
  /**
   * p:起始頂點
   * q:結束頂點
   * w:邊的權值
   * n:頂點個數
   */
  Scanner scan =new Scanner(System.in);
  int p,q;
  double w;
  System.out.println("請輸入結點的個數:");
  n=scan.nextInt();
  System.out.println("按照'A,B,C'的格式輸入邊與邊的信息,ABC分別代表邊的起始頂點,結束頂點,權值(輸入-1 -1 -1結束輸入):");
  while(true){
   p=scan.nextInt();
   q=scan.nextInt();
   w=scan.nextDouble();
   if(p<0||q<0||w<0)break;
   Edge e=new Edge();
   e.start=p;
   e.end=q;
   e.weight=w;
   edge.add(e);
  }
  for(int i=1;i<=n;++i){          //初始化邊的信息數組
   parent[i]=i;
  }
 }
 
 /*
  * 對象合并,將上一對象的結束邊作為下一對象的起始邊
  */
 public void union(int j,int k){
  for(int i=1;i<=n;++i){
   if(parent[i]==j)
    parent[i]=k;
  }
 }
 
 public void kruskal(){
  int i=0;                 //頂點
  while(i<n-1&&edge.size()>0){       //如果只有一條邊或者沒有邊跳出
   double min=Double.MAX_VALUE;
   Edge temp=null; 
   for(int j=0;j<edge.size();++j){      //遍歷圖形
    Edge tt=edge.get(j);
    if(tt.weight<min){           //若兩個頂點有權值,即相連
    min=tt.weight;     
    temp=tt;
    }
  }
  
  //構造一棵樹
  int jj=parent[temp.start];
  int kk=parent[temp.end];
  
  
  if(jj!=kk){
   ++i;                 //以end作為下一條邊的start,尋找下一條邊
   target.add(temp);           //將找到的邊放入目標集合中
   union(jj,kk);             
  }
  edge.remove(temp);           //將臨時邊刪除
  }
  System.out.println("最小生成樹的路徑是:");
  for(int k=0;k<target.size();++k){     //輸出最小生成樹
   Edge e=target.get(k);
   System.out.println(e.start+"-->"+e.end);
  }
 }
 
 public static void main(String[] args) {
  // TODO Auto-generated method stub
   kruskal kr=new  kruskal();
   kr.init();
   kr.kruskal();
 }
}
/*
 * start:起始頂點
 * end:結束頂點
 * weight:權值
 */
class Edge{
 public int start;
 public int end;
 public double weight;
}


調試結果如下:

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

來自:http://my.oschina.net/luckid/blog/378552

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!