并查集

是一种数据结构,常用于解决元素分组的问题

管理一系列不相交的集合,支持合并和查询两种操作

主要思想:用集合的一个元素代表集合,即祖宗代表家族

若是了解一些图论基础,可以发现并查集类似于一棵树,根节点就是祖宗,子节点就是集合的其他元素

基础代码

int f[N];
void init(int n) {
    for (int i = 1; i <= n; ++i) f[i] = i;
}
int find(int x) {
    if (f[x] == x)
        return x;
    else
        return find(f[x]);
}
void merge(int x, int y) {
    if (find(x) != find(y))
        f[find(x)] = f(y);
}

压缩路径

事实上,基础查询的合并效率是很低的,因为存在大量重复查询

这就要使用路径压缩,原理是在查询过程中,把沿途经过的每个节点的父节点都设为根节点

int f[N];
void init(int n) {
    for (int i = 1; i <= n; ++i) f[i] = i;
}
int find(int x) {
    return x == f[x] ? x : (f[x] = find(f[x]));
    
}
void merge(int x, int y) {
    if (find(x) != find(y))
        f[find(x)] = f(y);
}

注意:赋值运算符(=)的优先级没有三元运算符(?:)高,这里要加括号

按秩合并

因为并查集可以画成一棵树,也就意味着不同的集合存在深度差异

那么在合并时,是把深度小的合并到深度大的,还是相反?

当然是前者。从查询效率考虑,深度小的树的平均查询时间也小

这样做的方法也很简单,用一个rank数组记录每个根节点对应树的深度,初始化为1,合并时把rank值较小者往较大者上合并

int f[N], rank[N];
void init(int n) {
    for (int i = 1; i <= n; ++i) {
        f[i] = i;
        rank[i] = 1;
    }
}
int find(int x) {
    return x == f[x] ? x : (f[x] = find(f[x]));
    
}
void merge(int x, int y) {
    int fx = find(x), fy = find(y);
    if (rank[fx] <= rank[fy])
        f[fx] = fy;
    else
        f[fy] = fx;
    if (rank[fx] == rank[fy] && fx != fy)
        rank[fy]++;
}
posted @ 2024-07-08 22:14  clockleaf  阅读(13)  评论(0)    收藏  举报