您的位置:首页 > 其它

最小生成树 Prim算法和Kruskal算法

2019-05-17 11:26 176 查看

Prim算法和Kruskal算法都是基于最小生成树的MST性质的贪心策略。只是两者处理的对象不一样,一个是从点出发,一个是从边出发。

Prim算法

设源点为v1,初始时U={v1},假设补集V-U={v2,v3,v4,v5,v6}。
第一次操作从v1到v2…v5的五条路径长度中选一个最小的,选中的vi从V-U删除,加入U。
之后的操作类似,都是从所有u∈U,v∈V-U中找一个最短的边(u,v),将v加入U,并从V-U中删除。直到V-U空掉。

这里用了一个辅助数组closedge,closedge[i].lowcost用来记录当前U集合中到点 i 距离最小的点到点 i 的距离,adjvex用来记录这条最短的边是从哪个点来的。
源代码:

for(int i=1;i<=matrix.ver_num;i++){
closedge[i]=new Closedge();
closedge[i].adjvex=i;
if(i!=v){

closedge[i].lowcost=matrix.arc[v][i];
}else{
closedge[i].lowcost=0;
}
//System.out.print(closedge[i].lowcost+" ");
}
System.out.println("");
//当前选中的点
int temp_index=1;
for(int i=1;i<matrix.ver_num;i++){
int temp_dis=8888;
//找到非零的最小的那条边,确定下一个加入U的点
for(int j=1;j<=matrix.ver_num;j++){
if(closedge[j].lowcost!=0 && closedge[j].lowcost<temp_dis){
temp_dis=closedge[j].lowcost;
temp_index=j;
}
}
System.out.println(temp_index+"  "+temp_dis);
closedge[temp_index].lowcost=0;
//更新closedge数组
for(int k=1;k<=matrix.ver_num;k++){
if(closedge[k].lowcost!=0 && closedge[k].lowcost>matrix.arc[temp_index][k]){
closedge[k].lowcost=matrix.arc[temp_index][k];
closedge[k].adjvex=temp_index;
}
System.out.print(closedge[k].lowcost+"  ");
}
System.out.println("");
}

可以看出其时间复杂度是O(n^2),与边数无关,所以Prim算法更适合边稠密的图。

Kruskal算法

Kruskal算法又叫避环法。由于它是每次选权值最小的边加入进来,所以要考虑加入之后会不会成环的问题。
关于一个图里有没有环这个问题,我能想到的有关就是DFS,拓扑排序的源删除算法,并查集。
DFS操作比较简单,就是在深度优先搜索的基础上,加入对父结点的记录,如果对一个点搜索时搜索回到了其父结点,那就说明有环。它也可以用来找拓扑排序,顶点出栈的顺序反过来就是拓扑排序。
源删除算法是从点的入度这里下手。
并查集的话。。这个博客真的超有趣——超有趣的并查集详解
这里比较适合用并查集。

public void Kruskal_MST(int v){
//edge数组存储边信息
matrix.inputDirection();
//选择排序,按边的权值大小排序
for(int i=1;i<matrix.edge_num;i++){
int min_index=i;
for(int j=1+i;j<=matrix.edge_num;j++){
if(matrix.edge[j].value < matrix.edge[min_index].value){
min_index=j;
}
}
if(min_index!=i){
swap(matrix.edge,min_index,i);
}

}
//每把一条边添加进来都要判断是不是成环了
int[] vset = new int[matrix.ver_num+1];
int sum=0,count=0;
//辅助数组初始化
for(int i=1;i<=matrix.ver_num;i++){
vset[i]=i;
}

for(int j=1;j<=matrix.edge_num;j++){
//查找所在集合根find
int p1 = vset[matrix.edge[j].begin];
int p2 = vset[matrix.edge[j].end];
//不在相同集合时合并(把大的根作为这两个集合的代表)union
if(p1!=p2){
for(int i=1;i<=matrix.ver_num;i++){
if(vset[i]==Math.min(p1, p2)){
vset[i]=Math.max(p1, p2);
}
}
count++;
System.out.println("选中边: "+matrix.edge[j].begin+"->"+matrix.edge[j].end);
sum+=matrix.edge[j].value;
}
}
//用最小生成树边数和图的顶点数的关系来判断是不是构成了最小生成树
if(count==matrix.ver_num-1){
System.out.println("最小生成树总路径长度为:"+sum);
}else{
System.out.println("无法构成最小生成树");
}

}

这里用来测试的图是这个:

结果:

(这篇博客积压在草稿箱里快。。多少天了我也不知道,终于发了,草稿箱里还有好几篇/吐血)

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: