- 并查集(Disjoint Set):一种非常精巧而实用的数据结构。
- 用于处理不相交集合的合并问题。
- 经典应用:
- 连通子图
- 最小生成树Kruskal算法
- 最近公共祖先
应用背景:“帮派”
- 一个城市中有n个人,他们属于不同的帮派;
- 已知这些人的关系,例如1号、2号是朋友,1号,
- 3号也是朋友,那么他们都属于一个帮派;
- 问有多少帮派,每人属于哪个帮派。
- @用并查集可以很简洁地表示这个关系。
例:hdu 1213 How Many Tables
- 有n个人一起吃饭,有些人互相认识。
- 认识的人想坐在一起,而不想跟陌生人坐。
- 例如A认识B,B认识C,那么A、B、C会坐在一张桌子上。
- 给出认识的人,问需要多少张桌子。
并查集的操作
- 初始化
- 合并
- 查找
(1)初始化
- 定义int s[]是以结点i为元素的并查集。
- 初始化:令s[i]=i。 “一人一帮,我就是我!”(某人的号码是i,他属于帮派s[i]。)
(2)合并
- 例:加入第一个朋友关系(1, 2)。
- 在并查集s中,把结点1合并到结点2,
- 也就是把结点1的集1改成结点2的集2。
- 加入第二个朋友关系(1, 3):
- 查找结点1的集,是2,递归查找元素2的集是2;
- 把元素2的集2合并到结点3的集3。此时,
- 结点1、2、3都属于一个集。
- 加入第三个朋友关系(2, 4):
(3)查找
- 查找元素的集,是一个递归的过程,直到
- 元素的值和它的集相等,就找到了根结点的集。
- 这棵搜索树,可能很深,复杂度是O(n),
- 变成了一个链表,出现了树的“退化”现象。
void init_set(){ //初始化
for(int i = 1; i <= maxn; i++)
s[i] = i;
}
void union_set(int x, int y){ //合并
x = find_set(x);
y = find_set(y);
if(x != y) s[x] = s[y];
}
int find_set(int x){ //查找 (递归)
return x==s[x]? x:find_set(s[x]);
}
有多少个集(帮派)?
- 如果s[i] = i,这是一个根结点,是它所在的集的代表;
- 统计根结点的数量,就是集的数量。
复杂度
- 查找find_set()、合并union_set()的搜索深度
- 是树的长度,复杂度都是O(n)。
- 性能差。
- 能优化吗?
- -目标:优化之后,复杂度 < O(logn)。
@合并的优化
- 合并元素x和y时,先搜到它们的根结点;
- 合并这两个根结点:把一个根结点的集改成另一个根结点。
- 这两个根结点的高度不同,把高度较小的集合并
- 到较大的集上,能减少树的高度。
int height[maxn]; //用height[i]定义元素i的高度
void init_set(){
for(int i = 1; i <= maxn; i++){
s[i] = i;
height[i]=0; //初始化树的高度
}
}
void union_set(int x, int y){ //优化合并操作
x = find_set(x);
y = find_set(y);
if (height[x] == height[y]) {
height[x] = height[x] + 1; //合并,树的高度加1
s[y] = x;
}
else{ //把矮树并到高树上,高树的高度保持不变
if (height[x] < height[y]) s[x] = y;
else s[y] = x;
}
}
查询的优化:路径压缩
- 查询程序find_set():沿着搜索路径找到根结点,这条路径可能很长。
- 优化:沿路径返回时,顺便把i所属的集改成根结点。下次再搜,复杂度是O(1)。
路径压缩:递归实现
int find_set(int x){
if(x != s[x])
s[x] = find_set(s[x]); //路径压缩
return s[x];
}
- 路径压缩:整个搜索路径上的元素,在递归过程中,
- 从元素i到根结点的所有元素,它们所属的集都被改为根结点。
- 路径压缩不仅优化了下次查询,而且也优化了合并,因为合并时也用到了查询。
路径压缩:非递归实现
如果数据规模太大,用递归担心爆栈,可以这样写:
int find_set(int x){
int r = x;
while ( s[r] != r ) r=s[r]; //找到根结点
int i = x, j;
while(i != r){
j = s[i]; //用临时变量j记录
s[i]= r ; //把路径上元素的集改为根结点
i = j;
}
return r;
}
并查集习题
- poj 2524 Ubiquitous Religions,并查集简单题。
- poj 1611 The Suspects,简单题。
- poj 1703 Find them, Catch them。
- poj 2236 Wireless Network。
- poj 2492 A Bug's Life。
- poj 1988 Cube Stacking。
- poj 1182食物链,经典题。
- hdu 3635 Dragon Balls。
- hdu 1856 More is better。
- hdu 1272 小希的迷宫。
- hdu 1325 Is It A Tree。
- hdu 1198 Farm Irrigation。
- hdu 2586 How far away,最近公共祖先,并查集+深搜。
- hdu 6109 数据分割,并查集+启发式合并。