【数据结构】并查集
【并查集】
为实现 在 不相交集合 上的操作 (1.合并两个集合 2.查询某个元素属于哪个集合)而定义的一种数据结构
其实现有两种方式:链表和有根树
【应用】
在图论中 一个联通分量的所有点 对应一个集合
对应的操作可以为
判断两个点是不是在同一个联通分量之中
添加一条边合并两个联通分量
【优化】
(1)路径压缩(优化查找操作)
(2)通俗点说法就是要合并两个树,将树高度低的接到 高度高的树下, 使合并后的树的高度尽量小 (优化合并操作)
【模板】
此处用树来实现 用数组储存
/* 并查集模板 by chsobin */ int const maxn = 1000000; int fa[maxn]; //father void init(int n){ for(int i=0;i<=n;++i){ fa[i] = i; //初始时候设置每个集合只有一个元素,设置自己为集合代表元 } } int find(int u){ //找点u所在集合的代表元 if(fa[u] != u) fa[u] = find(fa[u]); return fa[u]; } int unin(int u, int v){ //返回0表示两个点已经在同一个联通分量 int fau = find(u); int fav = find(v); if(fau == fav) return 0; fa[fav] = fau; return 1; }
/*
更简短的并查集模板
by chsobin
*/
const int maxn = 1005; int fa[maxn]; void init(int n){ for(int i=0;i<=n;fa[i]=i++); } int find(int u){ return fa[u]==u ? fa[u] : fa[u] = find( fa[u] ); } int unin(int u, int v){ fa[find(v)] = find(u); }
/* 并查集模板优化 秩优化+路径压缩 by chsobin */ int const maxn = 1000000; int fa[maxn]; //father int rnk[maxn]; void init(int n){ for(int i=0;i<=n;++i){ fa[i] = i; //初始时候设置每个集合只有一个元素,设置自己为集合代表元 rnk[i] = 0; } } int find(int u){ //找点u所在集合的代表元 if(fa[u] != u) fa[u] = find(fa[u]); //优化1:路径压缩 return fa[u]; } int unin(int u, int v){ //返回0表示两个点已经在同一个联通分量 int fau = find(u); int fav = find(v); if(fau == fav) return 0; if(rnk[fau] > rnk[fav]) fa[fav] = fau; //优化2:将树矮的接到树高下 else { fa[fau] = fav; if(rnk[fau] == rnk[fav]) rnk[fav]++; } return 1; }
【题目】
hdu1213
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<iostream> using namespace std; const int maxn = 1005; int fa[maxn]; //father void init(int n){ for(int i=0;i<=n;++i){ fa[i] = i; //初始时候设置每个集合只有一个元素,设置自己为集合代表元 } } int find(int u){ if(fa[u] != u) fa[u] = find(fa[u]); return fa[u]; } int unin(int u, int v){ //返回0表示两个点已经在同一个联通分量 int fau = find(u); int fav = find(v); if(fau == fav) return 0; fa[fav] = fau; return 1; } int main(){ int t; cin >> t; while(t--){ int n, m; int u, v; cin >> n >> m; init(n); for(int i=0;i<m;++i){ cin >> u >> v; if( unin(u, v) == 1 ) n--; } cout << n << endl; } return 0; }
hdu1272
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/* 不仅要判断环,还要判断是否是树 无环,点数-1==边数 卡数据 0 0 时 */ #include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> using namespace std; /* 并查集模板 by chsobin */ int const maxn = 1000000; int fa[maxn]; //father int dot[maxn]; void init(int n){ for(int i=0;i<=n;++i){ fa[i] = i; //初始时候设置每个集合只有一个元素,设置自己为集合代表元 } } int find(int u){ //找点u所在集合的代表元 if(fa[u] != u) fa[u] = find(fa[u]); return fa[u]; } int unin(int u, int v){ //返回0表示两个点已经在同一个联通分量 int fau = find(u); int fav = find(v); if(fau == fav) return 0; fa[fav] = fau; return 1; } int main(){ int u, v; int edges = 0; init(100005); memset(dot,0,sizeof(dot)); int dn = 0; bool flag = true; while(cin >> u >> v && u!=-1 && v!=-1){ if(u==0 && v==0){ if(flag && edges == dn-1 || edges==0 && dn==0) cout << "Yes" << endl; else cout << "No" << endl; init(100005); memset(dot,0,sizeof(dot)); flag = true; dn = 0; edges = 0; continue; } ++edges; if( !dot[u] ){ dot[u] = 1; dn++; } if( !dot[v] ){ dot[v] = 1; dn++; } if(unin(u, v) == 0) flag = false; } return 0; }
更多题目:http://blog.csdn.net/zy691357966/article/details/41597947
【参考】
算法导论
顺便推荐一篇讲并查集比较有趣的博文