并查集 [维基百科]

来源:互联网 发布:外贸360软件 编辑:程序博客网 时间:2024/06/05 23:50

并查集是一种树型的数据结构,用于处理一些不相交集合(Disjoint Sets)的合并及查询问题。常常在使用中以森林来表示。


目录

  [隐藏] 
  • 1 主要操作
    • 1.1 合并两个不相交集合
    • 1.2 判断两个元素是否属于同一集合
  • 2 并查集的优化
    • 2.1 路径压缩
    • 2.2 Rank合并
  • 3 源代码
  • 4 时间及空间复杂度
  • 5 应用

[编辑]主要操作

需要注意的是,一开始我们假设元素都是分别属于一个独立的集合里的。

[编辑]合并两个不相交集合

操作很简单:先设置一个数组Father[x],表示x的“父亲”的编号。 那么,合并两个不相交集合的方法就是,找到其中一个集合最父亲的父亲(也就是最久远的祖先),将另外一个集合的最久远的祖先的父亲指向它。

Pascal代码:

function Union(x,y:integer):boolean;{其中GetFather是下面将讲到的操作} var fx,fy : integer;  begin     fx := getfather(x);     fy := getfather(y);     If fx=fy then exit(false)                 else Union:= true;    father[fx] := fy;{指向最祖先的祖先}  end;

C语言代码表示形式:

void Union(int x,int y){    fx = getfather(x);    fy = getfather(y);    if(fy!=fx)       father[fx]=fy;}

[编辑]判断两个元素是否属于同一集合

仍然使用上面的数组。则本操作即可转换为寻找两个元素的最久远祖先是否相同。可以采用递归实现。

Pascal代码:

Function Same(x,y:integer):boolean;begin  if GetFather(x)=GetFather(y) then     exit(true) else    exit(false);end;

C代码:

bool same(int x,int y){   return getfather(x)==getfather(y);}/*返回true 表示相同根结点,返回false不相同*/

[编辑]并查集的优化

[编辑]路径压缩

刚才我们说过,寻找祖先时采用递归,但是一旦元素一多起来,或退化成一条链,每次GetFather都将会使用O(n)的复杂度,这显然不是我们想要的。

对此,我们必须要进行路径压缩,即我们找到最久远的祖先时“顺便”把它的子孙直接连接到它上面。这就是路径压缩了。使用路径压缩的代码如下:

Function getfather(v:integer):integer;  begin    if (father[v]=v) then      getfather:=v    else      begin        father[v]:=getfather(father[v]);{路径压缩}        getfather:=father[v];      end;  end;
int getfather(int v){    if (father[v]==v)      return v;    else    {        father[v]=getfather(father[v]);//路径压缩        return father[v];     }}
Procedure Initialize;var  i:integer;begin  for i:=1 to maxv do     Father[i]:=i;end; Function GetFather(v:integer):integer;begin  if Father[v]=v then     exit(v) else    Father[v]:=getfather(father[v]);{路径压缩}  exit(Father[v]);end;

[编辑]Rank合并

合并时将元素所在深度低的集合合并到元素所在深度高的集合。

function judge(x,y:integer):boolean; var fx,fy : integer;  begin     fx := getfather(x);     fy := getfather(y);     If fx=fy then        exit(true) else        judge := false;     if rank[fx]>rank[fy] then       father[fy] := fx else begin       father[fx] := fy;       if rank[fx]=rank[fy] then          inc(rank[fy]);     end;  end;

初始化:

fillchar(rank,sizeof(rank),0);

C语言代码: 合并时将元素所在深度低的集合合并到元素所在深度深的集合。

void judge(int x ,int y) {     fx = getfather(x);     fy = getfather(y);      if (rank[fx]>rank[fy])        father[fy] = fx;     else     {        father[fx] = fy;        if(rank[fx]==rank[fy])           ++rank[fy];     }}

初始化:

memset(rank,0,sizeof(rank));

[编辑]源代码

加了所有优化的代码框架:

function getfather(v:longint):longint;begin  if father[v]=v then    exit(v) else     father[v]:=getfather(father[v]);    exit(father[v]);end;

[编辑]时间及空间复杂度

它使用O(n)的空间(n为元素数量),单次操作的均摊时间为O(\alpha(n))。其中\alpha(n)f(n)=A(n,n)的反函数,而A(n,n)是阿克曼函数。

[编辑]应用

Kruskal算法的优化