参考:勿在浮沙筑高台http://blog.csdn.net/luoshixian099/article/details/51908175
最小生成树
图的最小生成树是不重复的连接所有顶点所花的最低成本
如上图中图,最小生成树为粗黑线的边集合
Prim算法
思想:
{V}为已生成树的集合,lowcost保存每个顶点与已生成树集合中的所有顶点中最低的成本,adjvex保存每个顶点与{V}集合中最低成本关联的顶点,每次从lowcost中找到最低成本,在adjvex找到对应的关联的顶点加到集合{V}中,然后重置lowcost中的每个顶点的最低成本(与新加入的顶点的成本对比)
实现:
import java.util.Arrays;
// 图的最小生成树算法
public class MiniSpanTree {
private static int maxv = Integer.MAX_VALUE;
private static String[] vexs = new String[]{"v0","v1","v2","v3","v4","v5","v6","v7","v8"};
private static int vlen = vexs.length;
// 普利姆算法
// {param} arc 邻接矩阵
public static void prim(int[][] adjMatrix){
int[] lowcost = new int[vlen];
int[] adjvex = new int[vlen];
// 假设从顶点0开始查找,则最开始只有顶点0
// 所有顶点的关联顶点都是顶点0
for (int i=0;i<vlen;i++){
lowcost[i] = adjMatrix[0][i];
adjvex[i] = 0;
}
// 找到n-1条边即可
for (int i=1;i<vlen;i++){
int min = maxv;// 查找最小成本
int minv = 0;// 最小成本的相关顶点
for (int j=0;j<lowcost.length;j++){
// 如果最小成本为0,则认为已经在生成树中
if (lowcost[j]!=0&&lowcost[j]<min){
min = lowcost[j];
minv = j;// 相关顶点
}
}
System.out.printf("连接:%s----%s\n",vexs[minv],vexs[adjvex[minv]]);
lowcost[minv] = 0;// 置为0,已经查找完毕
// 重新计算lowcost,因为生成树集合有新加顶点minv
for (int k=0;k<vlen;k++){
if (lowcost[k]!=0&&adjMatrix[minv][k]< lowcost[k]){
lowcost[k] = adjMatrix[minv][k];
adjvex[k] = minv;
}
}
}
}
public static void main(String[] args){
int[][] adjMatrix = new int[][]{
{0,10,maxv,maxv,maxv,11,maxv,maxv,maxv},
{10,0,18,maxv,maxv,maxv,16,maxv,12},
{maxv,maxv,0,22,maxv,maxv,maxv,maxv,8},
{maxv,maxv,22,0,20,maxv,maxv,16,21},
{maxv,maxv,maxv,20,0,26,maxv,7,maxv},
{11,maxv,maxv,maxv,26,0,17,maxv,maxv},
{maxv,16,maxv,maxv,maxv,17,0,19,maxv},
{maxv,maxv,maxv,16,7,maxv,19,0,maxv},
{maxv,12,8,21,maxv,maxv,maxv,maxv,0}
};
System.out.println("prim alg");
prim(adjMatrix);
}
}
Karuskal算法
思想:
prim算法采用一步步查找与生成树中的最低成本的顶点,karuskal算法则采用查找最低成本边的思想。输入元素为边集数组,先对边集数组按照权重从小到达排序,则遍历边集数组,如果新遍历的边只要有一个顶点不在最小生成树中(防止形成环)则合并新查找到的边与之前的生成树
tree数组保存生成树信息
example:tree = [1,2,0,0,0]
则顶点0—1----2 当前为一棵树,如果多棵树的结尾元素不一致则可以认为他们不是一棵树,则进行合并
实现:
import java.util.Arrays;
// 图的最小生成树算法
public class MiniSpanTree {
private static int maxv = Integer.MAX_VALUE;
private static String[] vexs = new String[]{"v0","v1","v2","v3","v4","v5","v6","v7","v8"};
private static int vlen = vexs.length;
// kruskal算法使用边集
public static class Edge implements Comparable<Edge>{
int begin;
int end;
int weight;// 权重
public Edge(int begin,int end,int weight){
this.begin = begin;
this.end = end;
this.weight = weight;
}
@Override
public int compareTo(Edge o) {
return this.weight - o.weight;
}
}
// 卡鲁斯卡尔算法
// {param} edges:边集数组
// {param} vexNum:顶点数
public static void kruskal(Edge[] edges){
int[] tree = new int[vlen];// 生成树数组
// 初始化
for (int i=0;i<tree.length;i++)
tree[i] = 0;
// 边集边集数组
for (int i=0;i<edges.length;i++){
int begin = edges[i].begin;
int end = edges[i].end;
// 查找边的两个顶点在各自树中的结尾顶点
// 如果不一致则可以认为两个顶点不在一颗
// 树中,可以合并
int n = findTree(tree,begin);
int m = findTree(tree,end);
if (n!=m){
tree[n] = m;// 串联起来
System.out.printf("连接:%s----%s\n",vexs[begin],vexs[end]);
}
}
}
// 查找树的结尾结点
private static int findTree(int[] tree,int f){
while (tree[f]>0)
f = tree[f];
return f;
}
public static void main(String[] args){
Edge[] edges = new Edge[]{
new Edge(4,7,7),
new Edge(2,8,8),
new Edge(0,1,10),
new Edge(0,5,11),
new Edge(1,8,12),
new Edge(3,7,16),
new Edge(1,6,16),
new Edge(5,6,17),
new Edge(1,2,18),
new Edge(6,7,19),
new Edge(3,4,20),
new Edge(3,8,21),
new Edge(2,3,22),
new Edge(3,6,24),
new Edge(4,5,26),
};
System.out.println("kruskal alg");
kruskal(edges);
}
}
总结
prim算法从顶点出发,karuskal算法从边进行考虑,当顶点更多时适合使用karuskal算法,当边更多时使用prim算法更合适