java實現最小生成樹的prim算法和kruskal算法
在邊賦權圖中,權值總和最小的生成樹稱為最小生成樹。構造最小生成樹有兩種算法,分別是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);
}
}
測試結果如下:
在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;
}
調試結果如下:
本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!