最小生成樹的Prim演算法和Kruskal演算法java程式碼實現
阿新 • • 發佈:2019-02-18
MiniSpanTree中兩個靜態函式實現了最小生成樹的Prim演算法和Kruskal演算法:
package datastucture; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.Set; /** * 圖的最小樹生成演算法 * @author win7 * */ public class MiniSpanTree { /** * 求圖最小生成樹的PRIM演算法 * 基本思想:假設N=(V,{E})是聯通網,TE是N上的最想生成樹中的變得集合。演算法從U={u0}(u0屬於V), * TE={}開始,重複執行下述操作:在所有的u屬於U,v屬於V-U的邊(u,v)屬於E中找到一條代價最小 * 的邊(u0,v0)併入集合TE,同事v0併入U,直至U=V為止。此時TE中必有n-1條邊,則T=(V,{TE}) * 為N的最小生成樹。 * @param graph 圖 * @param start 開始節點 * @param n 圖中節點數 */ public static void PRIM(int [][] graph,int start,int n){ int [][] mins=new int [n][2];//用於儲存集合U到V-U之間的最小邊和它的值,mins[i][0]值表示到該節點i邊的起始節點 //值為-1表示沒有到它的起始點,mins[i][1]值表示到該邊的最小值, //mins[i][1]=0表示該節點已將在集合U中 for(int i=0;i<n;i++){//初始化mins if(i==start){ mins[i][0]=-1; mins[i][1]=0; }else if( graph[start][i]!=-1){//說明存在(start,i)的邊 mins[i][0]=start; mins[i][1]= graph[start][i]; }else{ mins[i][0]=-1; mins[i][1]=Integer.MAX_VALUE; } // System.out.println("mins["+i+"][0]="+mins[i][0]+"||mins["+i+"][1]="+mins[i][1]); } for(int i=0;i<n-1;i++){ int minV=-1,minW=Integer.MAX_VALUE; for(int j=0;j<n;j++){//找到mins中最小值,使用O(n^2)時間 if(mins[j][1]!=0&&minW>mins[j][1]){ minW=mins[j][1]; minV=j; } } // System.out.println("minV="+minV); mins[minV][1]=0; System.out.println("最小生成樹的第"+i+"條最小邊=<"+(mins[minV][0]+1)+","+(minV+1)+">,權重="+minW); for(int j=0;j<n;j++){//更新mins陣列 if(mins[j][1]!=0){ // System.out.println("MINV="+minV+"||tree[minV][j]="+tree[minV][j]); if( graph[minV][j]!=-1&& graph[minV][j]<mins[j][1]){ mins[j][0]=minV; mins[j][1]= graph[minV][j]; } } } } } /** * 求最小樹的Kruskal演算法 * 演算法思想:克魯斯卡爾演算法從另一個途徑求網中的最小生成樹。假設聯通網N=(V,{E}),則令 * 最小生成樹的廚師狀態為只有n個頂點而無邊的非連通圖T=(V,{}),途中每個頂點自稱一個連通分量。 * 在E中選擇代價最小的邊,若該邊衣服的頂點落在T中不同的連通分量上,則將此邊加入到T中,否則捨去此邊 * 而選擇下一條最小的邊。以此類推,直至T中所有的頂點都在同一連通分量上為止。 * @param V 圖中的節點集合 * @param E 圖中邊的集合 */ public static void KRUSKAL(int [] V,Edge [] E){ Arrays.sort(E);//將邊按照權重w升序排序 ArrayList<HashSet> sets=new ArrayList<HashSet>(); for(int i=0;i<V.length;i++){ HashSet set=new HashSet(); set.add(V[i]); sets.add(set); } System.out.println("++++++++++++++++++++++size="+sets.size()); for(int i=0;i<E.length;i++){ int start=E[i].i,end=E[i].j; int counti=-1,countj=-2; for(int j=0;j<sets.size();j++){ HashSet set=sets.get(j); if(set.contains(start)){ counti=j; } if(set.contains(end)){ countj=j; } } if(counti<0||countj<0) System.err.println("沒有在子樹中找到節點,錯誤"); if(counti!=countj){ System.out.println("輸出start="+start+"||end="+end+"||w="+E[i].w); HashSet setj=sets.get(countj); sets.remove(countj); HashSet seti=sets.get(counti); sets.remove(counti); seti.addAll(setj); sets.add(seti); }else{ System.out.println("他們在一棵子樹中,不能輸出start="+start+"||end="+end+"||w="+E[i].w); } } } public static void main(String [] args){ int [][] tree={ {-1,6,1,5,-1,-1}, {6,-1,5,-1,3,-1}, {1,5,-1,5,6,4}, {5,-1,5,-1,-1,2}, {-1,3,6,-1,-1,6}, {-1,-1,4,2,6,-1} }; MiniSpanTree.PRIM(tree, 0, 6); System.out.println("+++++++++++++++++++++++++++++++++"); int [] V={1,2,3,4,5,6}; Edge [] E=new Edge[10]; E[0]=new Edge(1,2,6); E[1]=new Edge(1,3,1); E[2]=new Edge(1,4,5); E[3]=new Edge(2,3,5); E[4]=new Edge(2,5,3); E[5]=new Edge(3,4,5); E[6]=new Edge(3,5,6); E[7]=new Edge(3,6,4); E[8]=new Edge(4,6,2); E[9]=new Edge(5,6,6); MiniSpanTree.KRUSKAL(V, E); } }
上面程式碼中還需要一個邊的輔助類Edge,程式碼如下:
package datastucture; public class Edge implements Comparable{ public int i,j,w; public Edge(int i,int j,int w){ this.i=i; this.j=j; this.w=w; } @Override public int compareTo(Object o) { Edge to=(Edge)o; if(this.w>to.w) return 1; else if(this.w==to.w) return 0; else return -1; } @Override public String toString() { return "start="+i+"||end="+j+"||w="+w; } }