森林与并查集
连通性问题
连通即集合,不可重复连通
Quick-Find算法
-
基于染色的思想,一开始所有点的颜色不同
-
连接两个点的操作,可以看成将一种颜色的点染成另一种颜色
-
如果两个点颜色一样,证明联通,否则不联通
用数组实现 判断连通 O ( 1 ) O(1) O(1) 合并操作 O ( N ) O(N) O(N)
问题思考:
- quick-find算法的联通判断非常快,可是合并操作非常慢
- 本质上问题中只是需要知道一个点与哪些点的颜色相同
- 而若干点的颜色可以通过间接指向同一个节点
- 合并操作时,实际上是将一棵树作为另一棵树的子树
#include<stdio.h>
#include<stdlib.h>
typedef struct UnionSet {
int *color;
int n;
} UnionSet;
UnionSet *init(int n) {
UnionSet *u = (UnionSet *)malloc(sizeof(UnionSet));
u->color = (int *)malloc(sizeof(int) * (n + 1));
u->n = n;
for (int i = 1; i <= n; i++) {
u->color[i] = i;
}
return u;
}
int find(UnionSet *u, int x) {
return u->color[x];
}
int merge(UnionSet *u, int a, int b) {
if (find(u, a) == find(u, b)) return 0;
int color_a = u->color[a];
for (int i = 1; i <= u->n; i++) {
if (u->color[i] - color_a) continue;
u->color[i] = u->color[b];
}
return 1;
}
void clear(UnionSet *u) {
if (u == NULL) return ;
free(u->color);
free(u);
return ;
}
int main() {
int n, m;
scanf("%d%d", &n, &m);
UnionSet *u = init(n);
for (int i = 0; i < m; i++) {
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
switch (a) {
case 1: merge(u, b, c); break;
case 2: printf("%s\n", find(u, b) == find(u, c) ? "Yes" : "No");
break;
}
}
clear(u);
return 0;
}
Quick-Union算法
核心思想:找代表元素
问题思考
- 极端情况下会退化成一条链
- 将节点数量多的接到少的树上面,导致了退化
- 将树高深的接到浅的上面,导致了退化
#include<stdio.h>
#include<stdlib.h>
typedef struct UnionSet {
int *father;
int n;
} UnionSet;
UnionSet *init(int n) {
UnionSet *u = (UnionSet *)malloc(sizeof(UnionSet));
u->father = (int *)malloc(sizeof(int) * (n + 1));
u->n = n;
for (int i = 1; i <= n; i++) {
u->father[i] = i;
}
return u;
}
int find(UnionSet *u, int x) {
if (u->father[x] == x) return x;
return find(u, u->father[x]);
}
int merge(UnionSet *u, int a, int b) {
int fa = find(u, a), fb = find(u, b);
if (fa == fb) return 0;
u->father[fa] = fb;
return 1;
}
void clear(UnionSet *u) {
if (u == NULL) return ;
free(u->father);
free(u);
return ;
}
int main() {
int n, m;
scanf("%d%d", &n, &m);
UnionSet *u = init(n);
for (int i = 0; i < m; i++) {
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
switch (a) {
case 1: merge(u, b, c); break;
case 2: printf("%s\n", find(u, b) == find(u, c) ? "Yes" : "No");
break;
}
}
clear(u);
return 0;
}
优化1
#include<stdio.h>
#include<stdlib.h>
#define swap(a, b) {\
__typeof(a) __temp = a;\
a = b; b = __temp;\
}
typedef struct UnionSet {
int *father, *size;
int n;
} UnionSet;
UnionSet *init(int n) {
UnionSet *u = (UnionSet *)malloc(sizeof(UnionSet));
u->father = (int *)malloc(sizeof(int) * (n + 1));
u->size = (int *)malloc(sizeof(int) * (n + 1));
u->n = n;
for (int i = 1; i <= n; i++) {
u->father[i] = i;
u->size[i] = 1;
}
return u;
}
int find(UnionSet *u, int x) {
if (u->father[x] == x) return x;
return find(u, u->father[x]);
}
int merge(UnionSet *u, int a, int b) {
int fa = find(u, a), fb = find(u, b);
if (fa == fb) return 0;
if (u->size[fa] < u->size[fb]) swap(fa, fb);
u->father[fb] = fa;
u->size[fa] += u->size[fb];
return 1;
}
void clear(UnionSet *u) {
if (u == NULL) return ;
free(u->father);
free(u->size);
free(u);
return ;
}
int main() {
int n, m;
scanf("%d%d", &n, &m);
UnionSet *u = init(n);
for (int i = 0; i < m; i++) {
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
switch (a) {
case 1: merge(u, b, c); break;
case 2: printf("%s\n", find(u, b) == find(u, c) ? "Yes" : "No");
break;
}
}
clear(u);
return 0;
}
优化2
#include<stdio.h>
#include<stdlib.h>
typedef struct UnionSet {
int *father;
int n;
} UnionSet;
UnionSet *init(int n) {
UnionSet *u = (UnionSet *)malloc(sizeof(UnionSet));
u->father = (int *)malloc(sizeof(int) * (n + 1));
u->n = n;
for (int i = 1; i <= n; i++) {
u->father[i] = i;
}
return u;
}
int find(UnionSet *u, int x) {
return u->father[x] = (u->father[x] == x ? x : find(u, u->father[x]));
}
int merge(UnionSet *u, int a, int b) {
int fa = find(u, a), fb = find(u, b);
if (fa == fb) return 0;
u->father[fb] = fa;
return 1;
}
void clear(UnionSet *u) {
if (u == NULL) return ;
free(u->father);
free(u);
return ;
}
int main() {
int n, m;
scanf("%d%d", &n, &m);
UnionSet *u = init(n);
for (int i = 0; i < m; i++) {
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
switch (a) {
case 1: merge(u, b, c); break;
case 2: printf("%s\n", find(u, b) == find(u, c) ? "Yes" : "No");
break;
}
}
clear(u);
return 0;
}