在计算机科学中,并查集是一种树型的数据结构,用于处理一些不交集的合并及查询问题。有一个联合-查找算法(union-find algorithm)定义了两个用于次数据结构的操做:node
- Find:肯定元素属于哪个子集。它能够被用来肯定两个元素是否属于同一子集。
- Union:将两个子集合并成一个集合。
for(int i=1;i<=n;i++) f[i]=i;
int find(int x) { while(f[x]!=x) x=f[x]; return x; }
void Union(int x1,int x2) { int t1=find(x1); int t2=find(x2); if(t1!=t2) f[t2]=t1; }
上面的代码看似简洁,可是每一次find操做的时间复杂度为O(H),H为树的高度,因为咱们没有对树作特殊处理,因此树的不断合并可能会使树严重不平衡,最坏状况每一个节点都只有一个子节点。c++
因此在find函数里采用路径压缩。算法
int find(int x) //查找x元素所在的集合,回溯时压缩路径 { if (x != f[x]) { f[x] = find(f[x]); //从x结点搜索到祖先结点所通过的结点都指向该祖先结点 } return f[x]; }
洛谷P3367【模板】并查集数据结构
#include<bits/stdc++.h> using namespace std; int n,m; int f[10002]; int find(int x) { if(x!=f[x]) f[x]=find(f[x]); return f[x]; } void Union(int x1,int x2) { int t1=find(x1); int t2=find(x2); if(t1!=t2) //祖先不同 f[t2]=t1; //把t2的祖先变为x1的祖先t1 } int main() { cin>>n>>m; for(int i=1;i<=n;i++) f[i]=i; for(int i=0;i<m;i++) { int z,x,y; cin>>z>>x>>y; if(z==1) Union(x,y); else { if(find(x)!=find(y))cout<<"N"<<endl; else cout<<"Y"<<endl; } } return 0; }
一个有n个结点的连通图的生成树是原图的极小联通子图,期包含原图的全部n个结点,且有保持图连通的最少边。函数
最小生成树其实就是最小权重生成树的简称。优化
Kruskal算法spa
例题:繁忙的都市code
#include<bits/stdc++.h> using namespace std; int n,m; int s,maxm; int p[100002]; struct node{ int u; int v; int c; }info[100002]; bool cmp(node x1,node x2) { if(x1.c!=x2.c)return x1.c<x2.c; else if(x1.u!=x2.u) return x1.u<x2.u; else return x1.v<x2.v; } int find(int x) //查找x元素所在的集合,回溯时压缩路径 { if (x!=p[x]) { p[x]=find(p[x]); } return p[x]; } void bcj(int x1,int x2)//把x2并入x1的集合 { int t1,t2;//存储祖先节点 t1=find(x1); t2=find(x2); if(t1!=t2)p[t2]=t1; } int main() { cin>>n>>m;//n就是顶点数,m是边数 for(int i=1;i<=n;i++) { p[i]=i; } for(int i=0;i<m;i++) { cin>>info[i].u>>info[i].v>>info[i].c; } sort(info,info+m,cmp); for(int i=0;i<m;i++)//遍历全部的边 { if(find(info[i].u)!=find(info[i].v)) { bcj(info[i].u,info[i].v);//把v并入u的集合 maxm=max(maxm,info[i].c); } } cout<<n-1<<" "<<maxm; return 0; }