思想
为使生成树上边的权值之和达到最小,则应使生成树中每一条边的权值尽可能地小。
【理解】
- 每一轮选一个最小的边,将边的顶点并入生成树中,直到全部顶点都在生成树中
- 显然,第一步是不完整的,如果选的边构成了环怎么办?所以,并入最小边的时候,还需要考虑是否形成环
- 若该边并入后形成环:那就不并入了,继续找下一个最小边
- 若该边不会形成环:并入,继续
【如何判断是否会构成环?】https://blog.csdn.net/summer_dew/article/details/83036947
性能
O(eloge),适合稀疏图(边少的图)
另一个,普利姆Prim O(n^2):适合稠密图(边多的图)
博客:https://blog.csdn.net/summer_dew/article/details/81660483
举例
初始:将所有的顶点纳入考虑(变黄色),所有的边纳入考虑(蓝色)
- 选取所有边中(蓝边)最小的边f-e(权重为3),纳入生成树的边(变棕色)
- 将边的两个顶点纳入生成树的顶点【e,f】
- 若生成树顶点中【e,f】有互联的线,变深蓝色,不考虑<=因为不能产生回边
- 当有n-1条边完成,退出

实现案例
例子来源:天勤论坛数据结构课程http://www.csbiji.com
【相关的存储结构】
- 左边的数组:存储着图的所有边与边的权重,用结点的下标代表顶点的信息(简化版的树,树的顶点信息为数字)
- 右边的数组:并查集,使树的双亲存储结构

//边的结构typedef struct{int a,b;int w; //权重}Road;Road road[maxSize];//并查集的结构int v[maxSize];//得到p的根结点int getRoot(int p) {while (p!=v[p]) //当p==v[p]时,即找到了根结点p = v[p];return p; //只有根结点的父亲是它自己}// 克鲁斯卡尔算法// road[]:边集// n:顶点数// e:边的个数int Kruskal(Road road[], int n, int e) {int sum=0;int a,b;int i;for (i=0; i<n; ++i) v[i]=i; //构造并查集的初始状态,各个节点为单独的树(即它的父亲都为它们自己)sort(road, e); //将边数组,从小到大排序for (i=0; i<e; ++i) { //遍历边数组,边数组已经被排序过了,从小到大-->从头到尾扫描即可a = getRoot(road[i].a);b = getRoot(road[i].b);if (a!=b) { //不会构成环v[a] = b; //更新并查集sum += road[i].w; //将边加入生成树}}}
