【并查集+压缩路径】

  并查集:(union-findsets)

一种简单的用途广泛的集合. 并查集是若干个不相交集合,能够实现较快的合并和判断元素所在集合的操作,应用很多,如其求无向图的连通分量个数等。最完美的应用当属:实现Kruskar算法求最小生成树。

 并查集的精髓(即它的三种操作,结合实现代码模板进行理解):

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

初始化后每一个元素的父亲节点是它本身,每一个元素的祖先节点也是它本身(也可以根据情况而变)。

/* 初始化集合*/
void Make_Set(int x)
{
    father[x] = x; //根据实际情况指定的父节点可变化
    rank[x] = 0;   //根据实际情况初始化秩也有所变化
}

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

查找一个元素所在的集合,其精髓是找到这个元素所在集合的祖先!这个才是并查集判断和合并的最终依据。
判断两个元素是否属于同一集合,只要看他们所在集合的祖先是否相同即可。
合并两个集合,也是使一个集合的祖先成为另一个集合的祖先。

优化一:

使用并查集查找时,如果查找次数很多,那么使用朴素版的查找方式肯定要超时。比如,有一百万个元素,每次都从第一百万个开始找,这样一次运算就是10^6,如果程序要求查找个一千万次,这样下来就是10^13,肯定要出问题的。

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

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

下面是采用路径压缩的方法查找元素:所谓路径压缩,就是查找的同时,顺路把该经过的元素都直接指向父节点,方便下次查找!这样以后再次查找时复杂度就变为O(1)

int find(int x)       //查找x元素所在的集合,回溯时压缩路径
{    
    if (x != parent[x])    
    {        
        parent[x] = find(parent[x]);     //回溯时的压缩路径    
    }         //从x结点搜索到祖先结点所经过的结点都指向该祖先结点    
    return parent[x];
}

上面是一采用递归的方式压缩路径, 但是,递归压缩路径可能会造成溢出栈,我曾经因为这个RE了n次,下面我们说一下非递归方式进行的路径压缩:

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;         //返回根节点的值            
}

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

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

                                如图.每个儿子都指向最顶的祖先,这样查询就变为O(1)

优化二:

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


void Union(int x, int y)
{
    x = Find_Set(x);
    y = Find_Set(y);
    if (x == y) return;
    if (rank[x] > rank[y]) 
    {
        father[y] = x;
    }
    else
        {
            if (rank[x] == rank[y])
            {
                rank[y]++;
            }
            father[x] = y;
        }
}

送上几道POJ好题:

http://162.105.81.212/JudgeOnline/problem?id=1703

http://162.105.81.212/JudgeOnline/problem?id=2421

http://162.105.81.212/JudgeOnline/problem?id=2492

http://162.105.81.212/JudgeOnline/problem?id=1861

http://162.105.81.212/JudgeOnline/problem?id=1308

http://162.105.81.212/JudgeOnline/problem?id=2524


部分转自http://www.cnblogs.com/vongang/

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值