并查集

定义在一些应用问题中,我们需要划分n个不同的元素成若干组,每一组的元素构成一个集合。这种问题的一个解决办法是,在开始时,让每个元素自成一个单元素集合,然后按一定顺序将属于同一组的元素所在的集合合并。其间要反复用到查找一个元素在哪一个集合的运算。适合于描述这类问题的抽象数据类型称为并查集


并查集支持以下三种操作:


1、Make_Set(x) 把每一个元素初始化为一个集合

  初始化后每一个元素的父亲节点是它本身,每一个元素的祖先节点也是它本身。

2、Find_Set(x) 查找一个元素所在的集合

   查找一个元素所在的集合,其精髓是找到这个元素所在集合的祖先!这个才是并查集判断和合并的最终依据。

   判断两个元素是否属于同一集合,只要看他们所在集合的祖先是否相同即可。

3、Union(x,y) 合并x,y所在的两个集合

   合并两个不相交集合操作很简单:
   利用Find_Set找到其中两个集合的祖先,将一个集合的祖先指向另一个集合的祖先。

实现方法

1.用编号最小的元素标记所在集合;

2.定义一个数组 set[1..n] ,其中set[i] 表示元素i 所在的集合;

1

2

3

4

5

6

7

8

9

10

1

2

1

4

2

6

1

6

2

2


Set[i] 不相交集合:{1,3,7}, {4}, {2,5,9,10}, {6,8}

并查集的优化

1、Find_Set(x)时 路径压缩


寻找祖先时我们一般采用递归查找,但是当元素很多亦或是整棵树变为一条链时,

每次Find_Set(x)都是O(n)的复杂度。

路径压缩,即当我们经过"递推"找到祖先节点后,"回溯"的时候顺便将它的子孙节点都直接指向祖先,

这样以后再次Find_Set(x)时复杂度就变成O(1)了。


朴素查找的代码,适合数据量不大的情况:

int findx(int x)
{    
    int r=x;   
    while(parent[r] !=r)
        r=parent[r];
    return  r;
}

下面是采用路径压缩的方法查找元素:

int find(int a)
{
    if(a!=f[a])
        f[a]=find(f[a]);       //回溯时压缩路径
    return f[a];
}

上面是一采用递归的方式压缩路径, 但是,递归压缩路径可能会造成溢出栈,

下面我们说一下非递归方式进行的路径压缩:


int find(int x)
{    
    int k, j, r;    
    r = x;    
    while(r != parent[r])     //查找跟节点        
       	r = parent[r];      //找到跟节点,用r记录下   
	k = x;           
	while(k != r)             //非递归路径压缩操作  
        {      
            j = parent[k];         //用j暂存parent[k]的父节点      
	    parent[k] = r;        //parent[x]指向跟节点       
	    k = j;                    //k移到父节点   
        }    
    return  r;         //返回根节点的值        
}


2、Union(x,y)时 按秩合并
即合并的时候将元素少的集合合并到元素多的集合中,这样合并之后树的高度会相对较小。

为了实现一个按秩合并的不相交集合森林,要记录下秩的变化。

对于每个结点x,有一个整数rank[x],它是x的高度(从x到其某一个后代叶结点的最长路径上边的数目)的一个上界。(即树高)。当由MAKE-SET创建了一个单元集时,对应的树中结点的初始秩为0,每个FIND-SET操作不改变任何秩。当对两棵树应用UNION时,有两种情况,具体取决于根是否有相等的秩。当两个秩不相等时,我们使具有高秩的根成为具有较低秩的根的父结点,但秩本身保持不变。当两个秩相同时,任选一个根作为父结点,并增加其秩的值路径压缩。


void Union(int a,int b)
{
    int fa,fb;
    fa=find(a);
    fb=find(b);
    if(rank[fa]>rank[fb])
        f[fb]=fa;
    else
        f[fa]=fb;
    if(rank[fa]==rank[fb])
        rank[fb]++;
}

若不考虑rank数组

void Union(int a,int b)
{
    int fa,fb;
    fa=find(a);
    fb=find(b);
    if(fa!=fb)
        f[fa]=fb;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值