并查集的路径压缩和按秩合并

记录一下并查集的两种优化:

路径压缩

路径压缩是并查集最常用的一个优化之一,他可以优化查询的速度。我们发现,在普通的并查集中,如果我们要询问一个元素所属哪个集合,我们只关心集合的代表是谁(也就是并查集树形结构中的树根是谁),而并不关心树的形态。那么我们可以在每次执行往上找根节点的时候,把路径上所有的元素的父亲全部指向树根(也就是通过改变树结构,降低了树的高度,增快了我们的查询速度),这样的均摊复杂度为 O(logN) O ( l o g N )
并查集的路径压缩和按秩合并_第1张图片

路径压缩后的查询函数

int findfa(int x)
{
    if(fa[x]==x) return fa[x];
    else return fa[x]=findfa(fa[x]);//fa[x]=findfa(fa[x])路径压缩
}

按秩合并

按秩合并是一种启发式合并,主要思想是合并的时候把小的树合并到大的树以减少工作量。
我们先来定义一下并查集的“秩”,有两种定义的方法:
1、树的高度
2、树的节点数
我们在路径压缩之后一般采用第二种,因为第一种在路径压缩之后就已经失去意义了,按照第二种合并可以减少一定的路径压缩的工作量。(但其实也不会太多,所以一般来说路径压缩就够用了)
单独采用按秩合并的话平摊查询时间复杂度同样为 O(logN) O ( l o g N )
如果我们把路径压缩和按秩合并合起来一起使用的话可以把查询复杂度下降到 O(α(n)) O ( α ( n ) ) ,其中 α(n) α ( n ) 为反阿克曼函数。阿克曼函数是一个增长极其迅速的函数,而相对的反阿克曼函数是一个增长极其缓慢的函数,所以我们在算时间复杂度的时候可以把他视作一个常数看待。

按树高为秩

void merge(int x,int y)
{
    int fx=findfa(x),fy=findfa(y);
    if(rank[fx]>rank[fy]) fa[fy]=fx;
    else
    {
        fa[fx]=fy;
        if(rank[fx]==rank[fy]) rank[fy]++;
//解释一下这儿,只有两颗树的高度相等的时候合并后高度才会增加
//因为你合并是把小的直接接到根节点上,深度只会+1,如果深度+1都没超过大的那肯定合并后的树高度不会增加
    }
}

按节点数为秩

void merge(int x,int y)
{
    int fx=findfa(x),fy=findfa(y);
    if(fx==fy)
        return;
    if(size[fx]>size[fy])
        swap(fx,fy);
    fa[fx]=fy;
    size[fy]+=size[fx];
}

你可能感兴趣的:(并查集)