并查集(用于不相交集合的数据结构)
并查集
并查集保持一组不相交的动态集合S={S1, S2, ..., SK}.每个集合通过一个代表来表示,代表即集合中的某个成员。
并查集的精髓(即它的三种操作):
集合中的每一个元素是由一个对象表示的,设x表示一个对象
MAKE-SET(x)
建立一个新的集合,其唯一成员(因而其代表)就是x。因为各个集合是不相交的,故要求x没有在其他集合中出现过。
初始化后每一个元素的父亲节点是它本身,每一个元素的祖先节点也是它本身
示例代码
#include <stdio.h> #include <stdlib.h> int father[1001]; int main() { int n, m, i, x, y; while (scanf("%d\n", &n) != EOF && n != 0) { // 初始化并查集 for (i = 1; i <= n; i ++) { father[i] = i; } } }
UNION(x, y)
将包含x和y的动态集合(比如说Sx和Sy)合并为一个新的集合(即这两个集合的并集).
合并两个不相交集合操作很简单:
利用FIND-SET找到其中两个集合的祖先,将一个集合的祖先指向另一个集合的祖先即可。
示例代码
void union_set(int x, int y) { int fx, fy; if (fx != fy) { fx = find_set(x); fy = find_set(y); } father[fx] = fy; }
FIND-SET(x)
返回一个指针,指向包含x的(唯一)集合的代表
查找一个元素所在的集合,其精髓是找到这个元素所在集合的祖先!这个才是并查集判断和合并的最终依据。
- 判断两个元素是否属于同一集合,只要看他们所在集合的祖先是否相同即可
- 合并两个集合,也是使一个集合的祖先成为另一个集合的祖先
示例代码
int find_set(int x) { while (father[x] != x) { x = father[x]; } return x; }
参考题目
题目描述: Mr Wang wants some boys to help him with a project. Because the project is rather complex, the more boys come, the better it will be. Of course there are certain requirements.Mr Wang selected a room big enough to hold the boys. The boy who are not been chosen has to leave the room immediately. There are 10000000 boys in the room numbered from 1 to 10000000 at the very beginning. After Mr Wang's selection any two of them who are still in this room should be friends (direct or indirect), or there is only one boy left. Given all the direct friend-pairs, you should decide the best way. 输入: The first line of the input contains an integer n (0 ≤ n ≤ 100 000) - the number of direct friend-pairs. The following n lines each contains a pair of numbers A and B separated by a single space that suggests A and B are direct friends. (A ≠ B, 1 ≤ A, B ≤ 10000000) 输出: The output in one line contains exactly one integer equals to the maximum number of boys Mr Wang may keep. 样例输入: 4 1 2 3 4 5 6 1 6 4 1 2 3 4 5 6 7 8 样例输出: 4 2
ac代码
#include <stdio.h> #include <stdlib.h> int father[10000001]; int number[10000001]; int max; int find_set(int x); void union_set(int x, int y); int main() { int i, n, x, y; while (scanf("%d", &n) != EOF) { // 初始化集合 for (i = 1; i <= 10000001; i ++) { father[i] = i; number[i] = 1; } // 合并并查集 max = 1; for (i = 0; i < n; i ++) { scanf("%d %d", &x, &y); union_set(x, y); } // 输出结果 printf("%d\n", max); } return 0; } int find_set(int x) { while (x != father[x]) { x = father[x]; } return x; } void union_set(int x, int y) { int fx, fy; fx = find_set(x); fy = find_set(y); if (fx != fy) { number[fy] += number[fx]; number[fx] = number[fy]; father[fx] = fy; if (number[fy] > max) { max = number[fy]; } } }