并查集
来源:互联网 发布:好弹幕软件下载 编辑:程序博客网 时间:2024/05/21 10:08
并查集作用:将众多元素按条件构造成一个集合,举个不好的例子,1,2,3,4,5都是数字而abc是字母,所以把他们分成2个集合,比较好理解的例子是百度上那个:
若某个家族人员过于庞大,要判断两个是否是亲戚,确实还很不容易,给出某个亲戚关系图,求任意给出的两个人是否具有亲戚关系。 规定:x和y是亲戚,y和z是亲戚,那么x和z也是亲戚。如果x,y是亲戚,那么x的亲戚都是y的亲戚,y的亲戚也都是x的亲戚。
并查集的基础操作有:①将每个元素都当成一个集合,此时每个元素的根节点都是自身②查找每个集合的根节点【代码里的find()】③合并相同根节点的集合
以下从图的角度理解,图中每个字母代表一个元素,箭头指向的为根节点【引用CYJB博客园图片】
初始化,每个元素(节点)指向自己:
查找:查找过程能起到路径压缩作用,见图,如果不压缩,那么要找d的根节点需要先找到c,再找到c的父节点...,路径压缩使多个节点直接指向根节点,而不需经过父节点(即父节点就是根节点)
合并:将两个根节点不同的集合合并,即让其中一个集合成为另一个集合原先根节点的新根节点
//以上图片来自网上博客
例题:杭电1213 How Many Tables
题意:某人要办生日,来的朋友不想和不认识的人坐同一张桌子,给定朋友数和朋友的认识关系,问至少需要多少桌子(A认识B,B认识C,那么ABC就是相互认识了)
#include<cstdio>#define MAX 1002int f[MAX];int find(int x){if( x == f[x] ) return x;return f[x] = find(f[x]);}void merge(int x,int y){x = find(x);y = find(y);if(x != y)f[x] = y; //将y的集合合并到x的集合}int main(){int i,n,m,t,a,b,res;//freopen("a.txt","r",stdin);while( ~scanf("%d",&t) ){while(t--){res = 0;scanf("%d%d",&n,&m);for(i=1;i<MAX;i++)//初始化假定每个人只认识自己 f[i] = i;while(m--){scanf("%d%d",&a,&b);merge(a,b);//输入ab,他们认识,合并在一同一集合 }for(i=1;i<=n;i++){ //判断有多少个集合就是有多少群人相互认识,就有多少桌子 if( f[i] == i ) res++;//判断集合数目是根据根节点指向自己 }printf("%d\n",res);}}return 0;}
这种递归形式的路径压缩find()有个缺点:数据大(如10W)时可能栈溢出,于是有了非递归的路径压缩find()
int find(int x){ if( x == f[x] ) return x; int rt=x; while(rt!=f[rt]) //找到根结点 rt=f[rt]; int fx; while(x!=rt) { fx=f[x]; f[x]=rt; x=fx; }return x;}
当我们希望集合不仅仅表示元素间是不是同属集合,还需要表示它们的关系时,就需要另开数组
POJ 2492 A Bug's Life
输入a,b表示ab异性,问是不是会造成矛盾,设r[]=0表示同性,r[]=1表示异性。
#include<stdio.h>const int maxn=2015;int f[maxn],r[maxn];void init(int n){ for(int i=1;i<=n;i++) { f[i]=i; r[i]=0; }}int find(int x){ if(x==f[x]) return x; int fx=f[x]; f[x]=find(f[x]); r[x]=(r[fx]+r[x])%2; return f[x];}void merge(int x,int y){ int fx=find(x); int fy=find(y); if(fx!=fy) { f[fy]=fx; r[fy]=(r[x]+r[y]+1)%2; }}#define sc(x) scanf("%d",&x);int main(){// freopen("1.in","r",stdin); int t; sc(t) for(int ce=1;ce<=t;ce++) { int n,m; sc(n);sc(m); init(n); int no=0; while(m--) { int x,y; sc(x) sc(y) if(no) continue; if(find(x)==find(y)) {//同一集合时(r[x]+r[y])%2为0就与输入的关系1矛盾 if(r[x]==r[y]) no=1; } else//不同集合的合并肯定不会有问题 merge(x,y); } if(no) printf("Scenario #%d:\nSuspicious bugs found!\n\n",ce); else printf("Scenario #%d:\nNo suspicious bugs found!\n\n",ce); } return 0;}
POJ 1182 食物链
#include <cstdio>#include <cmath>#include <cstring>#include <string>#include <set>#include <map>#include <stack>#include <queue>#include <vector>#include <iostream>#include <algorithm>using namespace std;#define ll long long#define eps 10^(-6)#define Q_CIN ios::sync_with_stdio(false);#define REP( i , n ) for ( int i = 0 ; i < n ; ++ i )#define FOR( i , a , b ) for ( int i = a ; i <= b ; ++ i )#define CLR( a , x ) memset ( a , x , sizeof (a) );#define RE freopen("1.in","r",stdin);#define WE freopen("1.out","w",stdout);#define MOD 10009#define debug(x) cout<<#x<<":"<<(x)<<emdl;const int maxn=50010;int f[maxn],r[maxn];//r=0:同类,r=1:被父亲吃,r=2:吃父亲int find(int x){ if(f[x]==x) return x; int fx=f[x]; //原父亲(fx) f[x]=find(f[x]); //ffx r[x]=(r[x]+r[fx])%3; return f[x];}void init(int n){ for(int i=1;i<=n;i++) { f[i]=i; r[i]=0; }}int merge(int x,int y,int d){ int fx=find(x); int fy=find(y); f[fy]=fx; //被 x 吃,所以以 x 的根为父 r[fy]=(3-r[y] + d-1 + r[x])%3;}#define sc(x) scanf("%d",&x);int main(){// RE int n,k,x,y,t,d; sc(n) sc(k) { int cnt=0; init(n); while(k--) { sc(d) sc(x) sc(y) if(x>n||y>n) cnt++; else if(d==2&&x==y) cnt++; else if(find(x)==find(y)) { if(d==1 && r[x]!=r[y])cnt++; //不同类 if(d==2 && (r[x]+1)%3!=r[y]) cnt++; } else merge(x,y,d); } printf("%d\n",cnt); }return 0;}
- HDU3938 并查集 并查集
- 并查集(集并查)
- HDU1232 并查集<并>
- 并查集
- 数据结构-并查集
- 并查集
- 并查集!
- 并查集
- 并查集
- 并查集
- 并查集
- 并查集总结
- 并查集学习
- 并查集
- 并查集
- 并查集
- 所谓并查集
- 并查集
- Windows下C++服务端和客户端Socket通信简单代码
- hadoop学习笔记1.使用shell和JAVA API操作HDFS
- Android_LogCat错误汇总
- 我的学习之旅:android自定义Dialog
- 杭电1021递归太严重,过不了
- 并查集
- 四种操作xml的方式: SAX, DOM, JDOM , DOM4J的比较
- Rating - HDU 4870 期望dp
- 杭电1021大神教的
- Android的webview加载本地html、本apk内html和远程URL
- Q13.2
- OpenGL ES Programming Guide for iOS 使用OpenGL ES和GLKit绘图
- XmlHelper
- poj1611 The Suspects