不相交集ADT

不相交集ADT

等价关系

等价关系是满足下列三个性质的关系R:

  1. (自反性)对于所有的(ain S,aRa)
  2. (对称性)aRb当且仅当bRa
  3. (传递性)若aRb且bRc,则aRc.

动态等价性问题

一个元素(ain S)的等价类是S的一个子集,它包含所有a有关系的元素。有两种运算允许进行。第一种是Find,返回包含给定元素的集合(即等价类)的名字。第二种运算时添加关系。使用求并运算Union,把含有a和b的两个等价类合并成一个新的等价类。

基本数据结构

因为我们的问题不要求Find操作返回任何特定的名字,而只是要求当且仅当两个元素属于相同的集合时,作用在这两个元素上的Find返回相同的名字。由于只需要父节点的名字,因此我们可以假设树被非显式地存储在一个数组中:数组中的每个成员P[i]表示元素i的父亲。如果i是根,那么P[i] = 0.

简单的初始化、Union、Find代码如下:

void Initialize( DisjSet S )
{
    int i;


    for( i = NumSets; i>0; i-- )
        S[i] = 0;
}

void SetUnion( DisjSet S, SetType Root1, SetType Root2 )
{
    S[ Root2 ] = Root1;
}

SetType Find( ElementType X, DisjSet S )
{
    if ( S[X] <= 0 )
        return X;
    else
        return Find(S[X], S);
}

灵巧求并

因为上面地Union算法相当地任意,可能会产生节点深度过深的情况。改进是借助于任意的方法打破现有关系。

  1. 按大小求并(union-by-size)
  2. 按高度求并(union-by-height)

其均保证了所有树的深度不超过O(Log N).

  1. 按大小求并:Union时按照大小(即节点总共的节点),每个根元素包含它树的大小的负值。Union时让小的树插在大的树上。
  2. 按高度求并:同上,只是Union时按照高度来做。

以下是按高度(秩)求并的程序

void SetUnionByHeight( DisjSet S, int Root1, int Root2 )
{
    if( S[Root2] < S[Root1] )
        S[Root1] = S[Root2];
    else
    {
        if( S[Root1] == S[Root2] )
            S[Root1]--;
        S[Root2] = Root1;
    }
}

路径压缩

路径压缩(path compression),是因为执行Union操作的任何算法都将产生相同的最坏情形的树,因为它会随意的打破树间的均衡。因此对整个数据结构重新加工并让算法加速的方法是改进Find。路径压缩的关键点在于:从X到根的路径上的每一个节点都使它的父节点变成根。

int FindByPathCompression( int X, DisjSet S )
{
    if( S[X] <= 0 )
        return X;
    else
        return S[X] = FindByPathCompression(S[X], S);
}
原文地址:https://www.cnblogs.com/evansyang/p/5964641.html