背景
并查集:顾名思义就是“合并集合”和“查找集合中的元素”两种操作合二为一的算法。考研中可以应用于克鲁斯卡尔算法中。
1,初始化
可以用数组实现并查集,定义并查集数组为:parent[ ]
对于每一个元素 parent[x]指向x在树形结构上的父亲节点。如果x是根节点,则令parent[x] = x。
2,查找
对于查找操作,假设需要确定x所在的的集合,也就是确定集合的代表元。可以沿着parent[x]不断在树形结构中向上移动,直到到达根节点。下面为代码:
int getRoot(int a)
{
while(a!=parent[a])
a=parent[a];
return a;
3,合并
对于两个集合,只需找到其中一个的根,使其成为另一集合中任意节点的孩子即可。
a=getRoot(a)
if(a!=b)
parent[a]=b;
4,判断两个节点是否在同一集合
找到两个节点的根,相同就是同一集合,不同就不是。
Kruskal算法
一开始并查集所有节点都各自独立,根节点为本身。
对图的边按权值从小到大排序。
循环从小到大读取每个边的两个节点a,b
获取a、b的根节点
如果a、b不属于同一集合,执行合并(第一次是将一条边记录在一个集合中;后面不断将新边融入集合,并且避免相同集合的边重复合并。)
如:已有0->2->1;那么0->1就不会被合并到该集合。
直到所有的边被检测完成。
typedef struct
{
int a,b;
int w;
}Road;//记录图的边,节点以及权重
Road road[Maxisize];
int v[Maxsize];//并查集数组
//利用并查集找节点的根
int getRoot(int a)
{
while(a!=v[a])
a=v[a];
return a;
}
void Kruskal(MGraph g,int &sum,Road road[])
{
int i,N,E,a,b;
N=g.n;
E=g.e;
sum=0;
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;
}
}
}