输入:创建图。
输出:组成最小生成树的所有边。
运行结果:
Kruscal算法:找权值最小的边,若并入后构成回路则舍弃。
设N=(V,{E})是连通图,求最小生成树。
零T={V,{}},各顶点自成一连通分量。
在E中找代价最小的边,若该边顶点落在不同连通分量上,则将其并入,依次类推到所有顶点到一个连通分量上。
总复杂度O(eloge),与n无关,适合稀疏图。
Kruscal算法是逐条边读入,检查是否构成回路,Prim算法是逐个顶点并入,根据并入顶点找最小边。
算法思路有了,可是现在还有一个问题,如何判断加入边后是否构成回
路呢?
我们可以用并查集。
看一下百科:
并查集,在一些有N个元素的集合应用问题中,我们通常是在开始时让每个元素构成一个单元素的集合,然后按一定顺序将属于同一组的元素所在的集合合并,其间要反复查找一个元素在哪个集合中。
关于并查集的原理可以参考此博客:
并查集详解
并查集实现:
int pre[MVNUM]; //记录各个结点的双亲
int find(int x){
//查找x的根节点
int r=x;
while(r!=pre[r]) //不断找双亲 直到找到根节点
r=pre[r];
int i=x,j;
while(i!=r){//路径压缩算法
j=pre[i]; //找到双亲就记录
pre[i]=r; //改变双亲为根节点
i=j;
}
return r;
}
算法实现:
邻接矩阵存储:
用Kruskal算法求网G最小生成树 输出各边,找权值最小的边 若并入后构成回路则舍弃.
void MiniSpanTree_Kruskal(MGraph G){
//用Kruskal算法求网G最小生成树 输出各边
//找权值最小的边 若并入后构成回路则舍弃
int i,k,j,f1,f2,total;
memset(a,0,sizeof(a)); //初始化
for(k=0;k%c\n",G.vexs[i],G.vexs[j]);
pre[f2]=f1; //修改双亲
total--;
}
a[i][j]=TRUE; //每次把这条边设为不可取
}
}
从图G中查找可以选择的代价最小的弧 用i,j返回两个结点的位置.
void MinArc(MGraph G,int &i,int &j){
//从图G中查找可以选择的代价最小的弧 用i,j返回两个结点的位置
int x,y;
double min=INFINITY;
for(x=0;xG.arcs[x][y].adj){ //可以选择 代价小
min=G.arcs[x][y].adj;
i=x;
j=y;
}
}
邻接表存储:
void MiniSpanTree_Kruskal(ALGraph G){
//用Kruskal算法求网G最小生成树 输出各边
//找权值最小的边 若并入后构成回路则舍弃
int i,k,j,f1,f2,total;
memset(a,0,sizeof(a)); //初始化
for(k=0;k%c\n",G.vertices[i].data,G.vertices[j].data);
pre[f2]=f1; //修改双亲
total--;
}
a[i][j]=TRUE; //每次把这条边设为不可取
}
}
从图G中查找可以选择的代价最小的弧 用i,j返回两个结点的位置。
void MinArc(ALGraph G,int &i,int &j){
//从图G中查找可以选择的代价最小的弧 用i,j返回两个结点的位置
int x;
double min=INFINITY;
ArcNode *p;
for(x=0;xadjvex]&&min>p->adj){ //可以选择 代价小
min=p->adj;
i=x;
j=p->adjvex;
}
p=p->nextarc;
}
}
}