并查集定义

并查集是一种用来处理不相交集合的树状数据结构。

用途

顾名思义,并查集主要有两个作用 1.并: 合并不相交的集合 2.查::查找集合的代表元素,用来检测集合是否相交。 一些非常常见的算法,如最小生成树,最近公共祖先等,都用到了并查集。

代表元

代表元是集合中用来代表整个集合某个原始,例如集合{1,2,3,4},可以设定1为该集合的代表元。集合内的所有元素,组织成以代表元为根的树状结构。代表元非常重要并查集的查找,其实就是查找代表元的过程,之后的合并操作,也是通过判断不同集合之间的代表元来进行的。

并查集的组成

并查集是用数组来保存的数状结构,数组用来保存父亲节点(或者前导节点)的信息。

const int N = 1005;
int pre[N];

 

并查集的三个算法

1. 初始化算法

初始化时,将所有节点的前导节点都设置为它自身。
void init()
{
    for(int i=0;i<N;i++)
        pre[i] = i;     
    return ;
}

 

2. 查找算法

从某个节点出发,一直查找它的前导节点,如果前导节点为自身,则代表该节点是这个集合的代表元素,另一方面,它也是这棵子树所在的根节点。

非递归版:

int find(x)
{
    int index=x;
    while(index!=pre[index])
        index = pre[index];
    return index;
}

递归版:

int find(x)
{
    int index = x;
    return index==pre[index]?index:find(pre[index]);
}

3. 合并算法

并查集的算法实际上是森林到树的转化过程。在执行合并算法的时候,我们同时将查找两个集合的代表元,也就是两棵树的根节点,然后将一颗树转化为另一棵树的子树,也就是将一棵树的根节点作为另一棵树的子节点。

void Union(x,y)
{
    if(find(x)!=find(y)) //两棵树属于不同的集合
    {
        pre[find(x)] = find(y); // 人为规定y所在的子树的根节点指向x所在子树的根节点
    }
}

优化算法——路径压缩

以上的。并查集用来解决的是集合之间相交问题,它关心的是集合的代表元是谁,而不关心集合内部的结构,换句话说,就是不关心树的形状。并查集查找的最终目的,是找到所在集合的代表元,也就是树的根节点。因此,可以在查找过程中,直接改变节点的前导

节点,使其指向代表元/根节点。

int find_pre(x)
{
    int index = x;
    if(index == pre[index])
        return index;
    else
        pre[index] = find_pre(pre[index]); // 更新
    return pre[index]; // 此时所有节点都指向代表元
}