我之前在无向图的连通性问题上使用到了并查集的方法。现在我系统整理一下。
并查集:
是什么:
它是一种用来管理元素分组情况的一个数据结构。
做什么:
- 查询元素a和元素b是否在同一组。
- 合并元素a和元素b所在的组。
注意:并查集虽然可以进行合并操作,但是无法进行分割操作。
结构:
并查集是使用树形结构实现的,但是不是二叉树。
每个元素对应一个结点,每个组对应一棵树。
实现过程
-
初始化:
用一个数组保存每个结点的父结点,最开始,每个结点的父结点都是自己,它们互相独立。没有边void Init() { for(int i=0; i<n; i++) { par[i] = i; } return 0; }
-
合并:
从一个组的根向另一个组的根连边,这样两棵树就变成一棵树,即将两个集合合并为一个集合。/*合并x和y所属的集合*/ void unite(int x, int y) { if(find(x)==find(y)) return; else{ par[find(y)] = find(x); } }
-
查询:
查询两个结点是否为同一个组:只要找到这两个结点的父结点是不是一个“人”,就能够知道是否为同一个组了。/*查找根节点*/ int find(int x) { int r = x; while(r!=pre[r]) r = pre[r]; return r; }
路径压缩:
如果一个树是长长的一棵,那么从叶子开始找它的根结点,那么复杂度就显得十分高了。所以我们要采用一个技巧——路径压缩,就是一旦找到每一个结点的根结点,直接把这个结点和它变成**“直属”**关系,即直接连到根上,这样下次再次查询就不会自顶向上一个个找了。
int find(int x)
{
int r = x;
while(r!=pre[r])
r = pre[r];
int i = x;//路径压缩
int j;
while(i != r)
{
j = pre[i];
pre[i] = r;
i = j;
}
return r;
}