一. 定义 并查集 (Disjoint set) 是一种树型数据结构,用于处理一些不相交集合的合并及查询问题,或者检查图上是否存在环。
Find:确定元素属于哪一个子集。它可以被用来确定两个元素是否属于同一子集。
Union:将两个子集合并成同一个集合。
性质:同一个集合的任意两个顶点相连会形成环
二. 分析 int find(int x):通过递归查找元素所在的根节点。
void union(int x,int y): 找到两个元素的根,并把其中一个元素(如x)的根节点设置为另一个元素(如y)的根节点,使得x所在的集合和y所在的集合合并。简单来说,就是让两个元素合并到相同的根。
模板
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 class UnionFind {public : vector<int >parent; UnionFind (int n) { parent.resize (n); for (int i = 0 ; i < n; i++) { parent[i] = i; } } int find_root (int x) { while (parent[x] != x) { parent[x] = find_root (parent[x]); x = parent[x]; } return x; } bool isConnect (int x, int y) { return find_root (x) == find_root (y); } void union_vertices (int x, int y) { int x_root = find_root (x); int y_root = find_root (y); if (x_root != y_root) { parent[x_root] = y_root; } } };
三. 题目 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 #include <stdio.h> #include <iostream> using namespace std;const int maxn = 1050 ;int s[maxn];int height[maxn];void init_set () { for (int i=1 ;i<=maxn;i++) { s[i] = i; height[i] = 0 ; } } int find_set (int x) { return x==s[x]?x:find_set (s[x]); } void union_set (int x,int y) { x = find_set (x); y = find_set (y); if (height[x] == height[y]) { height[x] = height[y]+1 ; s[y] = x; } else { if (height[x]<height[y]) { s[x] = y; } else s[y] = x; } } int main () { int t; cin >> t; while (t--) { int n,m; int x,y; cin >> n >>m; init_set (); for (int i=1 ;i<=m;i++) { cin >> x >> y; union_set (x,y); } int ans = 0 ; for (int i =1 ;i<=n;i++) { if (s[i] == i) ans++; } cout <<ans <<endl; } return 0 ; }
HDU1856
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 #include <iostream> #include <cstdio> using namespace std;const int maxn = 1e7 +7 ;int par[maxn],sum[maxn];void init_set () { for (int i=0 ;i<=1e7 ;i++) { par[i] = i; sum[i] = 0 ; } } int find_set (int x) { while (x!=par[x]) x = par[x]; return x; } void Union (int x,int y) { int fx = find_set (x); int fy = find_set (y); par[fy] = fx; int t; while (x!=fx) { t = par[x]; par[x] = fx; x = t; } } int main () { int n; int x,y; while (scanf ("%d" ,&n)!=EOF) { init_set (); int Maxn = 0 ; for (int i=1 ;i<=n;i++) { scanf ("%d %d" ,&x,&y); if (x>Maxn) Maxn = x; if (y>Maxn) Maxn = y; if (find_set (x)!=find_set (y)) Union (x,y); } int mmax = 1 ; for (int i=1 ;i<=Maxn;i++) sum[find_set (i)]++; for (int i=1 ;i<=Maxn;i++) { if (mmax<sum[i]) mmax = sum[i]; } printf ("%d\n" ,mmax); } }
POJ2524
输入n,m,分别表示学生个数和一共多少行,输出最多有多少不同的宗教信仰。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 #include <iostream> #include <cstdio> using namespace std;const int maxn = 50005 ;int par[maxn];int find_par (int x) { return par[x]==x?x:par[x] = find_par (par[x]); } void Union (int x,int y) { int fx = find_par (x); int fy = find_par (y); if (fx!=fy) par[fx]= fy; } int main () { int m,n; int x,y; int t=0 ; while (scanf ("%d%d" ,&n,&m)&&n) { for (int i=1 ;i<=n;i++) par[i] = i; for (int i=0 ;i<m;i++) { scanf ("%d%d" ,&x,&y); Union (x,y); } int ans = 0 ; for (int i=1 ;i<=n;i++) if (find_par (i)==i) ans++; printf ("Case %d: %d\n" ,++t,ans); } return 0 ; }