通过对并查集的学习发现,这是一个非常有用的高级数据结构,可以利用并查集来实现图中的最小生成树的Kruskal算法,非常有用。并查集用于处理不相交集合的合并问题,也可以看成对不同的连通分量之间的处理。例如以下问题:
1.一个城市中有n个人,他们属于不同的帮派
2.已知这些人的关系,例如1号,2号是朋友,1号,3号也是朋友,那么他们就属于同一个帮派。
用并查集就可以和你简介的表示这个关系即相互认识的人构成一个连通分量。
接下来开始讲解并查集的原理:
首先是初始化问题,即各自就单独成为一个帮派,自己就是帮主。
定义int father[]是以结点i为元素的并查集
initialize(){
for (int i=0;i<n;i++)
father[i] = i;
}
例如加入第一个关系,1和2是朋友关系,以此类推:
在并查集中,把节点1合并到结点2也就是father[1]改成2
将认识的人合并:
int find(int x)
{
return x==father[x] ? x : find(father[x]);
}
void union(int x,int y)
{
x = find(x);
y = find(y);
if (x != y)
father[y] = x;
}
通过以上的代码,并查集的代码实现就完成了。
但是,这样的“简版”并查集还有很多优化的地方。
#合并的优化以及路径的优化:
const int MAX = 1000;
int height[MAX]; //需要一个辅助的树的高度的数组
void initialize()
{
for (int i=1;i<=MAX;i++){
father[i] = i;
height[i] = 0;//空树高度为0
}
}
int find(int x)
{
if (x==father[x])
return father[x];
return father[x] = find(father[x]);
}
void union(int x,int y)
{
x = find(x);
y = find(y);
if (height[x] == height[y]){
father[x] = y;//将s[x]接到s[y]上
height[y]++;
}
else{
if (height[x] < height[y])
father[x] = y;
else
father[y] = x;
}
}
//由于find()函数是递归版,可能会爆栈
int find(int x)
{
int r = x;
while (r != father[r]) r = father[r];
while (x != r)
{
int tmp = father[s];
father[x] = r;
x = tmp;
}
return r;
}