今天细读了算法导论的21章不相交集合,也就是传说中的并查集,有点小收获。原来网上搜的并查集只介绍了路径压缩,今天第一次听说还有按秩合并这回事儿。算法导论上说,同时利用按秩合并和路径压缩技术处理,并查集的操作速度可以大大提升。设有一连串的make-set,find-set,union操作共m个,其中make-set有n个,在渐进意义上,这一连串的操作的时间复杂度为O(m*a(n)),a(n)是一个增长很慢的函数。具体证明不太懂,以后有时间再看。这里值得注意的一点就是union操作包含2个find-set操作和1个link操作。
再用并查集来分析一下kruskal算法,首先对于带权的边排序的效率为O(ElogE),make-set操作V次,union操作E次,因此总的时间复杂度为(V+E')a(V)+O(ElogE),a(v)=4,E'<V,总的复杂度可以简化为O(V+ElogE)=O(ElogE),换一种表示形式为O(ElogV),因为E<V*V
#include<iostream>
#include<algorithm>
using namespace std;
class union_find_set
{
public:
const static int maximum=1001;
int anc;
int father[maximum];
int count[maximum];
int rank[maximum];
union_find_set()
{
for(int i=0;i<maximum;i++)
{
this->father[i]=i;
this->count[i]=1;
this->rank[i]=0;
}
}
int getFather(int v)
{
if(father[v]==v)
{
return v;
}
else
{
return father[v]=getFather(father[v]);
}
}
bool same(int x,int y)
{
return (getFather(x)==getFather(y));
}
bool judge(int x,int y)
{
int fx,fy;
fx=getFather(x);
fy=getFather(y);
if(fx==fy) return false;
else
{
if(rank[x]<rank[y])
{
father[fx]=fy;
count[fy]+=count[fx];
}
else if(rank[x]>rank[y])
{
father[fy]=fx;
count[fx]+=count[fy];
}
else
{
father[fx]=fy;
count[fy]+=count[fx];
++rank[fy];
}
return true;
}
}
void init(int max)
{
for(int i=0;i<=max;i++)
{
this->father[i]=i;
this->count[i]=1;
this->rank[i]=0;
}
}
};
class node
{
public:
int x,y,cost,flag;
node()
{
flag=0;
}
};
int cmp(const void*a,const void*b)
{
node* A=(node*)a;
node* B=(node*)b;
if(A->cost>B->cost) return 1;
if(A->cost<B->cost) return -1;
return 0;
}
int main()
{
int n,m;
while(cin>>n>>m)
{
node p[m];
for(int i=0;i<m;i++)
{
cin>>p[i].x>>p[i].y>>p[i].cost;
}
qsort(p,m,sizeof(p[0]),cmp);
/*for(int i=0;i<m;i++)
{
cout<<p[i].x<<" "<<p[i].y<<" "<<p[i].cost<<endl;
}*/
union_find_set ufset;
ufset.init(n-1);
int max=0;
for(int i=0;i<m;i++)
{
if(ufset.judge(p[i].x,p[i].y))
{
p[i].flag=1;
if(p[i].cost>max)
max=p[i].cost;
}
}
cout<<max<<endl;
cout<<n-1<<endl;
for(int i=0;i<m;i++)
{
if(p[i].flag==1)
cout<<p[i].x<<" "<<p[i].y<<endl;
}
}
}