数据结构(一)

并查集

  • 原始版

第一步先初始化

int f[N];
inline void init(int n)
{
    for(int i=1;i<=n;i++)
        fa[i]=i;
}

假如有编号1,2,3,...,n,n个元素,我们用一个数组fa[]来储存每个元素的父节点(因为每个元素有且只有一个父节点,所以这是可行的),一开始都将父节点设为自己

第二步查询

int find(int x)
{
    if(fa[x]==x)
        return x;
    else 
        return find(fa[x]);
}

使用递归的写法实现对代表元素的查询:一层一层的访问父节点,直至根节点(根节点的标志就是父节点是本身)。要判断两个元素是否属于同一个集合,只需要看他们的根节点是否相同即可

第三步合并

inline void merge(int i,int j)
{
    fa[find(i)]=find(j);
}

先找到两个集合的代表元素,然后将前者的父节点设为后者即可,当然也可以将后者的父节点设为前者

  • 进阶版,路径压缩
    在每次查询时,把沿途的每个节点的父节点都设为根节点即可。在下次查询时就可以省事
    !!!注意:只有在每次查找时才进行路径压缩,因此初始化和合并的部分代码都相同
//标准版
int find (int x)
{
    if(x==fa[x])
        return x;
    else {
        fa[x]=find (fa[x]);
        return fa[x];
    }
}
//精简版
int find (int x)
{
    return x==f[a]?x:(fa[x]=find(fa[x]));
}
  • 进阶版 按秩合并
    按秩合并的原理就是应该把结构简单的树往结构复杂的树上合并,而不是相反,因为这样合并后到根节点距离变长的节点个数比较少
    开一个rank[]数组记录每个根节点对应的树的深度(如果不是根节点,其 rank相当于它作为根节点的子树的深度)。一开始,把所有元素的rank(秩)设为1。合并时比较两个根节点,把rank较小者往rank较大者合并。
    (注意:路径压缩和按秩合并如果一起使用,时间复杂度接近O(n),但可能会破坏rank的准确性)

初始化(按秩合并)

inline void init(int n)
{
    for(int i=1;i<=n;i++)
    {
        fa[i]=i;
        rank[i]=1;
    }
}

合并

inline void merge(int i,int j)
{
    int x=find(i),y=find(j);
    if(rank[x]<=rank[y])
        fa[x]=y;
    else 
        fa[y]=x;
    if(rank[x]==rank[y]&&x!=y)
        rank[y]++;
}
posted @ 2023-08-01 14:22  liuwansi  阅读(4)  评论(0编辑  收藏  举报