最小生成树之kruskal算法
最小生成树:将一个带权值的有n个点的无向图只用n-1个边将所有点都连接起来但不形成回路,当这n-1个边所有的权值相加起来最小时,这个n个点和n-1个边形成的图就是最小生成树
从策略上来说,Prim算法是直接查找,多次寻找邻边的权重最小值,而Kruskal是需要先对权重排
序后查找的~
所以说,Kruskal在算法效率上是比Prim快的,因为Kruskal只需一次对权重的排序就能找到最小生
成树,而Prim算法需要多次对邻边排序才能找到~
prim:该算法的时间复杂度为O(n2)。与图中边数无关,该算法适合于稠密图。
kruskal:需要对图的边进行访问,所以克鲁斯卡尔算法的时间复杂度只和边又关系,可以证明其时
间复杂度为O(eloge)。适合稀疏图
Kruskal算法和Prim算法其实都是基于贪心,不过Kruskal算法需要先对边的权值排序,而Prim算法
是每次都选取当前权值最小的边
kruskal算法:
原理:简单的贪心算法
大致实现过程:
首先我们把所有的边按照权值先从小到大排列,接着按照顺序选取每条边,如果这条边的两个端点不属于同一集合,且将这两条端点连接上之后在无向图中不会构成环,那么就将这条边纳入最短生成树,直到所有的边都被选完,最小生成树即形成
如何判断两个端点不属于同一个集合?
可以使用深度搜索,但效率过慢,改用并查集
只要一条边的两个端点不属于同一集合,将这条边连接起来就不会在无向图中形成环
代码:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define size 10001
struct edge { //边的结构体
int u;
int v;
int w;//u,v分别为该条边的两个顶点,w为该条边的权值
} bian[size];
int n, m;//点和边
int connect[size];
void init() {//并查集初始化
for (int i = 1; i <= n; i++) {
connect[i] = i;
}
}
int find(int x) {//查找端点属于的集合
return connect[x] == x ? x : connect[x] = find(connect[x]);
}
void unions(int a, int b) {//将两个端点形成的边连接起来
connect[find(b)] = find(a);
}
int cmp(const void*p1, const void *p2) { //给结构体排序的函数
return ((struct edge *)p1)->w - ((struct edge *)p2)->w;
}
int main() {
scanf("%d %d", &n, &m);
for (int i = 0; i < m; i++) {
scanf("%d %d %d", &bian[i].u, &bian[i].v, &bian[i].w);
}
init();
qsort(bian, m, sizeof(struct edge), cmp);//根据边的权值排序
int count = 0; //统计连入多少条边
int length = 0; //最小生成树长度
for (int i = 0; i < m && count < n - 1; i++) { //从小到大枚举每一条边
if (find(bian[i].u) != find(bian[i].v)) { //这条边没有连接起来,两个端点不属于同一集合,即该边没有并入最小树
unions(bian[i].u, bian[i].v);//将这条边连接起来
length += bian[i].w;
count++;
}
}
printf("%d",length);
}
经典例题:洛谷 P2121 拆地毯
另外有两个用prim算法写的题:
洛谷 P2872 Building Roads S
洛谷 P1991 无线通信网
题解链接: http://t.csdn.cn/QXDIG