并查集是一种用来管理元素分组情况的数据结构。一般用来检查图的连通性。并查集通过树形结构实现,每一个元素对应一个节点,每一个组对应一棵树。
(1)初始化
准备n个节点来表示n个元素,最开始没有边。
(2)合并
从一个组的根向另一个组的根连边,这样两棵树就成了一棵树。
(3)查询
目的是为了知道两个节点是否属于同一个组,我们沿着树向上走,来查询这棵树的根是什么。如果两个节点走到了同一个根,则它们是同一组。
(4)退化
树形结构如果发生退化,那么复杂度就会很高,因此需要避免。我们在合并时,如果两棵树的高度不同,则高度小的向高度大的树连边。
(5)路径压缩
对于每个节点,一旦向上走到了根结点,就把该节点直接和根节点相连。
(6)实现
CODE
#define MAXN 100
class DisjointSet
{
private:
int par[MAXN];
int rank[MAXN];
public:
void init(int n)
{
for (int i = 0; i < n; ++i)
{
par[i] = i;
rank[i] = 0;
}
}
int find(int x)
{
if (par[x] == x)
return x;
else
return par[x] = find(par[x]);
}
void unite(int x, int y)
{
x = find(x);
y = find(y);
if (x == y)return;
if (rank[x] < rank[y])
par[x] = y;
else
{
par[y] = x;
if (rank[x] == rank[y])++rank[x];
}
}
bool same(int x, int y)
{
return find(x) == find(y);
}
};
实战应用:
POJ1182
CODE
void solve1182()
{
int ans = 0;
DisjointSet D;
for (int i = 0; i < K; ++i)
{
int t = T[i];
int x = X[i] - 1, y = Y[i] - 1;
if (x < 0 || N <= x || y < 0 || N <= y)
{
++ans; continue;
}
if (t == 1)
{
if (D.same(x, y + N) || D.same(x, y + N * 2))
++ans;
else
{
D.unite(x, y);//x-A union y-A
D.unite(x + N, y + N);
D.unite(x + N * 2, y + N * 2);
}
}
else
{
if (D.same(x, y) || D.same(x, y + N * 2))
++ans;
else
{
D.unite(x + N, y);
D.unite(x, y + N * 2);
D.unite(x + N * 2, y);
}
}
}
printf("%d\n", ans);
}