模板一:kk给出的有路径压缩的模板
1 #include<bits/stdc++.h>//万能头文件 2 using namespace std; 3 4 const int maxn=1010; 5 int fa[maxn]; 6 7 void init(){ 8 int n=1000; 9 for(int i=1;i<=n;i++){ 10 fa[i]=i; 11 } 12 }//赋初值 13 14 int find(int x){ 15 return x==fa[x]? x : fa[x]=find(fa[x]); 16 }//寻找祖先节点 17 18 void baba(int x,int y){ 19 int fx = find(x); 20 int fy = find(y); 21 fa[fx] = fy; 22 }//交朋友 23 24 int size[1000]={0}; 25 for(int i=1;i<=n;i++){ 26 size[find(i)]++; 27 }//计算每个祖宗节点团体内有几个人 28 29 int main(){ 30 init();//初始化 31 int a,x,y; 32 while(scanf("%d %d %d",&a,&x,&y)){ 33 if(a==1){ 34 baba(x,y); 35 } 36 else{ 37 if(find(x)!=find(y)) printf("NO\n"); 38 else printf("YES\n"); 39 } 40 } 41 return 0; 42 }
模板二:没有优化过的简单并查集(之前网上找的,其实都还不懂class是什么,但是并查集的思想是学到了)
1 //未优化并查集 2 3 #include<vector> 4 #include<iostream> 5 using namespace std; 6 7 class DisjSet{ 8 private: 9 std::vector<int> parent; 10 11 public: 12 DisjSet(int max_size) : parent(std::vector<int>(max_size)){ 13 for(int i=0;i<max_size;++i) 14 parent[i] = i; 15 } 16 17 int find(int x){ 18 return parent[x]==x? x : find(parent[x]); 19 } 20 21 void to_union(int x,int y){ 22 parent[find(x)] == find(x2); 23 } 24 25 bool is_same(int x,int y){ 26 return find(x) == find(y); 27 } 28 };
模板三:既有路径压缩也有按秩排序优化的模板(同样也是网上的)
1 //并查集的优化 2 3 //一:按秩合并 4 // 用“秩”(rank)代替高度,秩表示高度的上界,通常情况我们令只有一个节点的树的秩为0。 5 // 两棵秩分别为r1、r2的树合并,如果秩不相等,我们将秩小的树合并到秩大的树上, 6 // 这样就能保证新树秩不大于原来的任意一棵树。如果r1与r2相等,两棵树任意合并,并令新树的秩为r1 + 1。 7 8 //二:路径压缩 9 // 在执行Find的过程中,将路径上的所有节点都直接连接到根节点上。 10 11 #include<vector> 12 class DisjSet{ 13 private: 14 std::vector<int>parent; 15 std::vector<int>rank;//秩 16 17 public: 18 DisjSet(int max_size) : parent(std::vector<int>(max_size)), 19 rank(std::vector<int>(max_size,0)){ 20 for(int i=0;i<max_size;i++) 21 parent[i] = i; 22 } 23 24 int find(int x){ 25 return x == parent[x]? x : (parent[x] = find(parent[x])); 26 } 27 28 void to_union(int x,int y){ 29 int fx = find(x); 30 int fy = find(y); 31 if(rank[fx] > rank[fy]) 32 parent[fy] = fx; 33 else{ 34 parent[fx] = fy; 35 if(rank[fx] == rank[fy]) 36 rank[fy]++; 37 } 38 } 39 bool is_same(int x,int y){ 40 return find(x) == find(y); 41 } 42 };
模板四:消化吸收后,自己用简单函数的形式,又写了一个
1 #include<bits/stdc++.h> 2 using namespace std; 3 4 const int size = 1010; 5 int parent[size],rank[size]; 6 7 //赋初值 8 void parentkey(){ 9 for(int i=0;i<size;i++) 10 parent[i] = i; 11 } 12 13 //找根节点 14 int find(int x){ 15 return x==parent[x]? x : parent[x]=find(parent[x]); 16 } 17 18 //合并 19 void parent_union(int x,int y){ 20 int fx = find(x); 21 int fy = find(y); 22 if(rank[fx] > rank[fy]) 23 parent[fy] = fx; 24 else{ 25 parent[fx] = fy; 26 if(rank[fx]==rank[fy]) 27 rank[fy]++; 28 } 29 } 30 31 //计数 32 int number(int x){ 33 int num = 0; 34 int fx = find(x); 35 for(int i=0;i<size;i++){ 36 if(fx==find(i)) 37 num++; 38 } 39 return num; 40 } 41 42 int main() 43 { 44 parentkey();//初始化 45 int a,x,y; 46 printf("输入一个数字(1为合并,非1为判断),输入两个想要合并或判断的节点:"); 47 while(scanf("%d%d%d",&a,&x,&y)!=EOF){ 48 if(a==1) 49 parent_union(x,y); 50 else{ 51 if(find(x)==find(y)) printf("YES\n"); 52 else printf("NO\n"); 53 } 54 } 55 int n; 56 printf("输入你想要查找的该节点所属团体的总节点数:"); 57 while(scanf("%d",&n)!=EOF) 58 printf("%d\n",number(n)); 59 }
并查集并没有用到什么STL的工具,只是单单一个数组,就能起到一个分类的作用,一下是并查集的几个要点:
(1)数组的意义,这里定义的parent[]的数组,只是作为一个标签的作用,初始化时值随数组的下标定义,简单方便,代表各节点相互独立,没有瓜葛。
(2)函数parent_union(int x, int y),顾名思义,union联合,通过改变数组parent里的值,使两个两个拥有同一个值,代表这两个属于同一类,起到分类作用。在这个模板的这个函数中,便体现了按秩排序的优化,用秩rank来表示每个节点的高度,在联合时,先判断两个节点的的高度大小,把高度小的依附于高度大的节点,起到尽可能缩小这棵树的高度。
(3)函数find(int x,int y),用于判断两个节点是否相关,是否因为前面的union联合以后归属于同一类。在这个函数中,用到了路径压缩的优化,在优化之前,可以说是一个父节点携带一个子节点,一旦节点一多,将会造成树过长的麻烦,在判断两个节点是否相关时,会使遍历树非常慢,浪费时间。路径压缩就是将所有的子节点依附于同一个父节点,这样在判断起来时只有O(1)的时间复杂度,快了很多。代码的重点在于 parent[x]=find(parent[x])。
(4)函数num(int x)用于查出节点x所属类型一共有多少节点。
并查集中解决函数递归可能造成重复而引起不必要的时间复杂度使用的是路径压缩的方法,而在dp中则是用一个数组将值存放起来,方法可以不同,目的都是为了降低时间复杂度。
原文:https://www.cnblogs.com/0424lrn/p/12213270.html