最早接触并查集的时候是在做一道最小生成树问题上,当时还不会并查集,题解说用克鲁斯卡尔算法,用并查集来维护,就能够完成最小生成树。
并查集是什么呢?其实,并查集就是一个集合,它有两种操作,一个是合并(merge),一个是查找(getf)。 合并就是说把具有相同祖先的集合合并成
为一个集合,查找就是说,查找某些具有相同祖先的集合。当然这个祖先只是我这样叫的(他其实并不是名义上的祖先),很多时候我们会维护很多
这样的信息,比如说,在求最小生成树的算法中,我们维护的是检查两个顶点是否属于同一个集合,也就是说,判断他们是不是联通,如果联通的话,
那就肯定不能连接这条边的,因为在一个图中,要想找到一棵树,是不能有环存在的(这个到最小生成树的时候再说).
然后就是效率问题了,我们知道在查找操作中,肯定会因为n的数目过大而超时,所以我们需要用到路径压缩的知识,防止出现一种奇怪的树,也
就是全部左偏或者全部右偏而有需要我们对树的叶子节点进行getf()操作。
先来到有关并查集的最为简单的题目了。
现在有n个人,分别编号为1,2,3,,,n,然后给出m个关系,比如说:1 2 ,那么1和2就是一个同伙,输出最小的同伙数。
# include<cstdio> # include<iostream> using namespace std; # define MAX 12345 int f[MAX]; int n,m; int sum; void init() { for ( int i = 1;i <= n;i++ ) { f[i] = i; } } int getf( int v ) { if ( f[v]==v ) { return v; } else { f[v] = getf(f[v]); return f[v]; } } void merge( int v,int u ) { int t1 = getf(v); int t2 = getf(u); if ( t1!=t2 ) { f[t2] = t1; } } int main(void) { cin>>n>>m; init(); for ( int i = 0;i < m;i++ ) { int t1,t2; cin>>t1>>t2; merge(t1,t2); } for ( int i = 1;i <= n;i++ ) { if ( f[i]==i ) { sum++; } } cout<<sum<<endl; return 0; }
有关并查集的东西,以后再更新~