最小生成树的概念:一个用n-1条边连接的树,且所有点到其他点的路径唯一的一条路径叫做最小生成树。
Kruskal算法
Kruskal算法是用来求解最小生成树问题的算法之一。
Kruskal算法从边出发,利用贪心的思想,先使用快速排序算法进行预处理排序,之后选择边权最小邻边。
Kruskal 算法是能够在O(mlogm)的时间内得到一个最小生成树的算法。
① 将边按照边权从小到大排序,并建立一个没有边的图T。
② 选出一条没有被选过的边权最小的边。
③ 如果这条边两个顶点在T 中所在的连通块不相同,那么将 它加入图T, 相同就跳过。
④ 重复②和③直到图T 连通为止。
由于只需要维护连通性,可以不需要真正建立图T,可以用并查集 来维护。
算法虽简单,但需要相应的数据结构来支持……具体来说,维护一个森林,查询两个结点是否在同一棵树中,连接两棵树。
抽象一点地说,维护一堆 集合,查询两个元素是否属于同一集合,合并两个集合。
其中,查询两点是否连通和连接两点可以使用并查集维护。
主体代码
void Kruskal(){
sort(edge,edge + m,cmp);//排序是关键,自带时间复杂度
for(int i = 0;i < m;i++){//枚举所有节点
int fx = find(edge[i].x);
int fy = find(edge[i].y);
if(fx == fy)continue;
ans += edge[i].z;
f[fx] = fy;//路径压缩
cnt++;
if(cnt == n - 1)break; //连通图具有n - 1条边
}
}
并查集部分
int find(int x){
if(x != f[x])
return f[x] = find(f[x]);
return f[x];
}
Prim算法
Prim 算法是另一种常见并且好写的最小生成树算法。该算法的基本思想是从一个结点开始,不断加点(而不是 Kruskal 算法的加边)。
struct point{
int x,y,l;
}edge[MAXN];
void Prim(){
int pos;
dis[0] = 0;
for(int i = 0;i < n;i++){
double min = 1e8;
for(int j = 0;j < n;j++){
if(!vis[j] && dis[j] < min){
min = dis[j];
pos = j;
}
}
ans += min;
vis[pos] = true;
for(int j= 0;j < n;j++){
if(vis[j] == 0 && edge[j].l < dis[j]){
dis[j] = edge[j].l;
}
}
}
}
算法异同
Prim在稠密图中比Kruskal优,在稀疏图中比Kruskal劣。Prim是以更新过的节点的连边找最小值,Kruskal是直接将边排序。
两者其实都是运用贪心的思路
本篇作为2022CCF赛前复习
请登录后发表评论
注册
停留在世界边缘,与之惜别