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