使用场景
并查集常用于操作不相交集合,判断或操作相关联关系,类似将森林中的两棵树进行合并
C 实现
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;
}
// O(1)
int find(UnionSet *u, int x) {
return u->color[x];
}
// 将颜色 a 染成颜色 b, O(n)
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;
}
union_set
算法,链表合并,并通过路径压缩进行优化
#include <stdio.h>
#include <stdlib.h>
#define swap(a, b) { \
__typeof(a) __temp = b; \
b = a; a = __temp; \
}
typedef struct UnionSet {
int *father;
// int *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;
}
// 一般都是 O(1), 出现路径压缩的情况会有些损失
int find(UnionSet *u, int x) {
return u->father[x] = (u->father[x] == x ? x : find(u, u->father[x]));
}
// 一般都是 O(1),在 find 基础上进行合并
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;
}
C++ 实现
#include <bits/stdc++.h>
using namespace std;
class DisjointSet {
private:
// rank 节点数量来判断谁对谁进行合并更佳,路径更短
int *father, *rank;
public:
DisjointSet(int size) {
father = new int[size];
rank = new int[size];
for (int i = 0; i < size; ++i) {
father[i] = i;
rank[i] = 0;
}
}
~DisjointSet() {
delete[] father;
delete[] rank;
}
int find_set(int node) {
if (father[node] != node) {
// 路径优化
father[node] = find_set(father[node]);
rank[node] = rank[father[node]] + 1;
}
return father[node];
}
bool merge(int node1, int node2) {
int ancestor1 = find_set(node1);
int ancestor2 = find_set(node2);
if (ancestor1 != ancestor2) {
// 谁节点数量多优先选谁做父节点
if (rank[ancestor1] > rank[ancestor2]) {
swap(ancestor1, ancestor2);
}
father[ancestor1] = ancestor2;
rank[ancestor2] = max(rank[ancestor2], rank[ancestor1] + 1);
return true;
}
return false;
}
}