并查集的实现与优化
LeetCode
上有一道名为朋友圈
的题目,如下:
班上有 N 名学生。其中有些人是朋友,有些则不是。他们的友谊具有是传递性。如果已知 A 是 B 的朋友,B 是 C 的朋友,那么我们可以认为 A 也是 C 的朋友。所谓的朋友圈,是指所有朋友的集合。
给定一个 N * N 的矩阵 M,表示班级中学生之间的朋友关系。如果M[i][j] = 1,表示已知第 i 个和 j 个学生互为朋友关系,否则为不知道。你必须输出所有学生中的已知的朋友圈总数。
读完题目要求,暂时能想到的是,如果要解决该问题,首先要判断出在这N
名学生中谁跟谁是朋友关系,其次是,对于关系的划分,有哪些学生属于这一组,而哪些学生属于另外一组。那基本的数据组织和数据操作该如何实现呢?
并查集,作为一种用于解决不相交集的合并及查询问题的树型数据结构,非常适合用来处理类似于上述例子的问题。顾名思义,并查
代表着两种不同的操作,并
用来表示合并两个不同集合的操作,查
用来表示检索两个不同元素是否属于同一个集合。
如果按照并查集的定义进行数据结构和数据操作的声明,那就有了以下代码实现
typedef struct UnionSet {
int *data;
int n;
} UnionSet;
UnionSet *initUnionSet(int n) {
UnionSet *u = (UnionSet *)malloc(sizeof(UnionSet));
u->n = n;
u->data = (int *)malloc(sizeof(int) * (n + 1));
for ( int i = 0; i < n; i++ ) {
u->data[i] = i;
}
return u;
}
int find(UnionSet *u, int x) {
return u->data[x];
}
void merge(UnionSet *u, int a, int b) {
int fa = find(u, a), fb = find(u, b);
if ( fa