您的位置:首页 > 其它

并查集(Union-Find)算法介绍

2014-10-11 21:18 211 查看
并查集

1.并查集是一种树型的数据结构,用于处理一些不相交集合的合并问题。

并查集的主要操作有

1.合并两个不相交集合(Union(int x,int y))

2.判断两个元素是否属于同一个集合(Find(int x))

3.路径压缩

定义一个数组father,用father[i]表示元素i的父亲结点.

初始化每个集合只有其本身(编号从1-N):

for (int i=1; i<=n; i++)
{
father[i]=i;
}
即对于节点i,它的组号也是i。

合并操作(Union(int x,int y)),father[i]=j,表示j是i的父亲(即i,j属于同一集合)











查找操作(Find(int x))

查找两个元素是否属于同一集合(father[x]=x表示该元素没有合并过,自身为一个集合)

int Find(int x)   //查找x所属集合
{
while (x!=Father[x])
{
x=Father[x];
}
return Father[x];
}


根据上面就可以写成合并操作了

void Union(int x,int y)
{
x=Find(x);   //查找所属集合
y=Find(y);
if(x!=y)   //不在同一个集合
{
Father[x]=y;  //合并
}
}


又如:



上面所有元素最终形成三个集合。

合并操作的改进
最上面的Union的执行是相当任意的,它通过使得第二课棵树的子树而完成合并,对其进行简单的改进是借助任意的方法打破现有的关系,使得总让较小的树成为较大的树的子树,这种方法叫做按大小求并。
进行一次任意的并的结果(分别Union(5,6),Union(7,8),Union(5,7)
后Union(4,5)):



按大小求并的结果:



即总是size小的树作为子树和size大的树进行合并。这样就能够尽量的保持整棵树的平衡。

在初始情况下,每个组的大小都是1,因为只含有一个节点,所以我们可以使用额外的一个数组来维护每个组的大小,对该数组的初始化也很直观:
for (int i = 0; i < N; i++)
{
sz[i] = 1;    // 初始情况下,每个组的大小都是1
}
而在进行合并的时候,会首先判断待合并的两棵树的大小,然后按照上面图中的思想进行合并,实现代码:

void Union(int x, int y)
{
int i = find(x);
int j = find(y);
if (i == j) return;
// 将小树作为大树的子树
if (sz[i] < sz[j])
{
Father[i] = j;
sz[j] += sz[i];
}
else
{
Father[j] = i;
sz[i] += sz[j];
}
}


形象化(上面的文字及此图来自这里):



另一种实现方法为按高度求并,我们跟踪每棵树的高度而不是大小并执行那些Union使得浅点的树成为高点的树的子树,因为只有两棵相同深度的的树求并时树的高度才增加(此时树的高度增加1)。
void Union(int x,int y)
{
if(height[x]==height[y])
{
height[x]=height[x]+1;
Father[y]=x;
}
else if(height[x]<height[y])
{
Father[y]=x;
}
else
{
Father[x]=y;
}
}

路径压缩
通过 路径压缩使得结点离根更加的近,一个未采用路径压缩的例子:



路径压缩后:



这样采用路径压缩后使得Find更加的高效。
实现代码:
int Find(int x)
{
if(x!=Father[x])
{
Father[x]=Find(Father[x]);   //递归找根进行更新
}
return Father[x];
}

点这里并查集应用
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: