1. 程式人生 > >最小生成樹的Prim演算法和Kruskal演算法java程式碼實現

最小生成樹的Prim演算法和Kruskal演算法java程式碼實現

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;
	}
}