kruskal是用来找连通图中的最小生成树,简单点来说,就是对于n个点,找n-1条边,将n个点连接起来,使得边的权值相加最小,这样构成的树就叫最小生成树
算法过程描述
- 将所有边按照权值进行排序
- 遍历所有边,按照权值从小到大选取。
- 选取的一个边看其顶点是否处于一个连通集中,如果属于,则放弃该边,如果不属于,选取该边进入生成树中,并将两顶点所处集合合并
- 产生的生成树为最小生成树
算法证明
对于一个点x,所有与这个点直接相连的边中最小的边必定是最小生成树的一条边:假设该边为x->y,如果不选该边,那么必定有x->a->y,而x->y < x->a,包含x->a->y的最小生成树便不是最小生成树.同理,对于任一连通图,与该连通图直接相连的所有边中最小的边一定是最小生成树的一条边
算法模板
int father[n];
class edge{
public:
int u, int v, int w;
};
void init(){
for(int i=0; i<n; i++){
father[i] = i;
}
}
int find(int x){
int r = father[x];
while(father[r] != r){
r = father[r];
}
// 路径收缩
int i=x, j;
while(father[i] != i){
j = father[i];
father[i] = r;
i = j;
}
return r;
}
void unions(int x, int y){
int fx = find(x);
int fy = find(y);
if(fx != fy){
father[fx] = fy;
}
}
void kruskal(){
init();
sort(edges.begin(), edges.end());
for(int i=0; i<edges.size(); i++){
int u = edges[i].u;
int v = edges[i].v;
int w = edges[i].w;
int fu = find(u);
int fv = find(v);
if(fu!=fv){
ans.push_back(edges[i]);
unions(fu, fv);
}
}
}