算法:互斥集合

    表示互斥集合(disjoint set)时,经常会使用另一种具有独特形态的树结构--并查集(union-find)数据结构。

互斥集合:

        假设有n名客人参加聚会,主持人要求相同生日的人组成一队。话音刚落,客人们立刻开始组队。刚开始时,因为不知道哪位客人的生日与自己的生日相同,所以大家只能单独徘徊。不过,只要找到1名相同生日的客人,两人就会开始结伴移动。若发现另一个相同生日的队伍,就会与这支队伍合并。

        应该使用何种数据结构表示这种情况呢?对参加聚会的全体客人的集合而言,队伍是将些集合分割为若干部分的子集合。而每个队由生日相同的客人组成,所以每名客人都不可能属于一个以上的队伍。这种没有共同元素的子集合称为互斥子集合,保存这种信息并对其进行操作的数据结构就是并杳集数据结构。

       为了表示这种情况,首先需要把客人表示为0到n-1之间的元素,然后生成只包含1个元素的n个集合。两名客人a和b的生日相同时,合并包含二者的集合。为了实现该过程,需要如下3种运算。

      1、初始化:初始化为n个元素被包含于各自集合的形式

      2、并集(union)运算:给出两个元素a和b时,合并两个元素所在集合

      3、查找(find)运算:给出了某个元素a时,返回此元素所在集合

利用数组表示互斥集合:

     利用一维数组就能非常简单地表示互斥集合。生成如下形式的数组belongsTo。

             belongsTo[i] = 第i个元素所属的集合序号

     此方式并集运算耗时,虽然查找O(1)

利用树结构表示互斥集合:

     同属于一个集合的元素绑定到一个树结构。

struct NaiveDisjointSet {

    vector<int>   parent;

    NaiveDisjointSet(int n) : parent(n) {

        for (int i=0; i<n; i++)    parent[i] = i;

    }

    int  find(int u)  const  {

        if (u == parent[u])  return u;

        return find(parent[u]);

    }

    void merge(int u, int v) {

         u = find(u);   v = find(v);

         if (u == v)    return;

         parent[u] = v;

     }

};

互斥集合的优化:

     如果最后生成高度为n-1的树,则是链表,那么合并和查找运算都会耗费O(n)的运算时间。

     最简单的方法是,合并两个树时,把高度更低的树添加到高度更高的树,以限制树高的增加

struct OptimizedDisjointSet {

    vector<int>   parent, rank;

    NaiveDisjointSet(int n) : parent(n) , rank(n, 1) {

        for (int i=0; i<n; i++)    parent[i] = i;

    }

    int  find(int u)  const  {

        if (u == parent[u])  return u;

        return parent[u] = find(parent[u]);

    }

    void merge(int u, int v) {

         u = find(u);   v = find(v);

         if (u == v)    return;

        if (rank[u] > rank[v])   swap(u, v);

         parent[u] = v;

       if (rank[u] == rank[v])  ++rank[v];

     }

};

时间复杂度:并集为O(lgn),查找为O(lgn)

另一种优化方法是路径压缩优化


示例:判断图的连通性

示例:追踪最大集合






  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值