Skip to content

并查集

初始化

将每个数字看成一个部落,每个数字的父节点为自身,部落的根节点为首领,即自己为自己部落的首领

cpp
int f[N];

for(int i = 1; i <= n; i++) {
    f[i] = i;
}

FindHead操作 (查找部落首领)

当一个数字的父节点是自己的时候,说明自己就是“首领”, 直接返回;否则,就递归查询父节点的父节点。

查询过程中,若层数过多,每次查询都要一层层向上查找,比较费时间,可以进行路径压缩,使得一个节点的父节点即为该节点的“首领”。这样,下次最多只需经过一次向上查询就能找出某个数的“首领”。

  • 路径压缩前

  • 路径压缩后

cpp
int find(int n)
{
    if(f[n] == n) {
        return n;
    }else {
        f[n] = find(f[n]); // 路径压缩
        return f[n];
    }
}

Merge操作 (合并两个部落)

让一个“部落首领”跟从另一个“部落首领”

  • 合并前

  • 合并后

  • 合并 + 路径压缩

在合并的时候需要进行FindHead操作来找到“部落首领”,而FindHead操作的时候即可进行路径压缩。

cpp
void merge(int a, int b)
{
    f[find(a)] = f[find(b)];
}

Test操作 (查询两个人是否在同一个部落)

判断两个数否在同一个“部落”,只需要看两个数的“首领”是否一致。如果是,则在同一个“部落”,反之则不在。

  • 例: 在下图中

    • 1, 2, 3, 4的“首领”都为3,所以它们是在一个“部落”
    • 5, 6, 7的“首领”都为7,所以它们在同一个“部落”
    • 2的“首领”是3,6的“首领”是7,所以它们不在同一“部落”
cpp
bool test(int a, int b)
{
    return find(a) == find(b);
}