并查集
树结构,主要用来合并两个集合以及用来判断两个元素是否在一个集合。
判断方法:一个集合只能存在一个根结点。给定两个元素,查找其根结点,如果两者根结点相同,那么说明这两个元素属于同一个集合。
基本操作
/* 初始化 */
int father[maxn], n; // 父结点数组
void initialize()
{
for (int i = 0; i < n; i++)
father[i] = i; // 每个元素是一个集合
}
/* 寻根结点 */
int findRoot(x)
{
while (x != father[x])
x = father[x];
return x;
}
/* 合并集合 */
void union(int a, int b)
{
int rootA = findRoot(a);
int rootB = findRoot(b);
if (rootA != rootB) // 根结点不同,则属于不同集合
father[rootA] = rootB; // 合并两集合
}
路径压缩
将每一个结点的父结点直接设为集合的根节点,这样可以避免并查集退化成直链,使查询复杂度降至 O ( 1 ) O(1) O(1)。
/* 递归写法 */
int findRoot(x)
{
if (x == father[x]) // x即为根结点
return x;
int root = findRoot(father[x]); // 先找到根节点
father[x] = root; // 当前元素父结点设为根节点
return root;
}
/* 非递归写法 */
int findRoot(x)
{
int root = x;
while (root != father[root]) // 找到根节点
root = father[root];
while (x != father[x]) // 遍历路径,路径上所有结点父结点设为根节点
{
int temp = x;
x = father[x];
father[temp] = root;
}
return root;
}