【算法与数据结构】不相交集合——并查集

来自coursera的课程:普林斯顿大学的算法

通俗地讲,在一堆item上进行两种操作,一是合并,即将某两个item所在的集合合并为一个大集合;二是查询,即给定的两个item是否属于同一个集合。

高效快速地支持这种操作的数据结构就是并查集。


先看数学原理:

等价关系与等价类

从数学上看,等价类是一个对象(或成员)的集合,在此集合中的所有对象应满足等价关系。若用符号"≡"表示集合上的等价关系,那么对于该集合中的任意对象x,y, z,下列性质成立:

1、自反性:x ≡ x

2、对称性:若 x ≡ y 则 y ≡ x

3、传递性:若 x ≡ y 且 y ≡ z 则 x ≡ z

因此,等价关系是集合上的一个自反、对称、传递的关系。


常用实现:

常用的实现方法是采用森林结构,即相同集合的item同属于一个树。

使用树节点加权优化后,可以保证树的高度不超过logN(其中N为item数量);再加上路径压缩优化,平均复杂度可以在常数级别。

class UinonFind
{
public:
    UinonFind(int N)
    {
        NUM = N;
        id = new int[N]; 
        sz = new int[N]; 
        for (int i = 0; i < N; i ++)
            id[i] = i, sz[i] = 1;
    }
    int root(int i)
    {
        while (i != id[i])
        {       
            id[i] = id[id[i]]; //优化1:路径压缩
            i = id[i];
        }       
        return i;
    }
    int connected(int p, int q)
    {
        return root(p) == root(q);
    }
    int connect(int p, int q)
    {
        int i = root(p);
        int j = root(q);
        //优化2: 加权树
        if (sz[i] < sz[j]) {id[i] = j; sz[j] += sz[i];} 
        else               {id[j] = i; sz[i] += sz[j];} 
    }
private:
    int *id;
    int *sz;
    int NUM;
};

例题:

给定N个整数{0,1,2,...,N-1}的集合,以及一些在该集合上的请求:

1) remove x 。 即将x从集合中删除

2) find x。即在集合中不小于x的最小整数,例如集合为{1, 2, 4,5},find 3,结果为4.


分析:

可以用并查集来解决这个问题。用并查集来维护相邻的被删除的集合,例如:{0, 1, 2, 3, 4}中删除掉{1,2},那么并查集数据结构中id[]可能是

id[] = {0, 1, 1, 3, 4}。我们只需要为每个id[i]=i的节点增加一个信息,来保存该子树中最大的值。

以5个整数的集合做个演示:

操作id[]max[]
初始{-1,-1,-1,-1,-1}
{-1,-1,-1,-1,-1}
remove 1{-1,1,-1,-1,-1}
{-1,1,-1,-1,-1}
remove 4
{-1, 1, -1, -1, 4}
{-1, 1,-1,-1,4}
remove 2
{-1, 1,1,-1,4}
{-1,2,-1,-1,4}


当执行find x操作时,如果id[x]==-1,即结果为x;否则,另id[id[...id[x]...]]为x的根root,那么root+1即为结果,如果root==N,则无解。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值