并查集学习(2)

 实现这个数据结构主要有三个函数:如下:

void UFset()   //初始化
{
for(int i=0;i<N;i++)
   parent[i]=-1;
}



int Find(int x)   //返回第X节点所属集合的根结点
{
for(int i=x;parent[i]>=0;i=parent[i]);
while(i!=x)    //优化方案――压缩路径
{
   int tmp=parent[x];
   parent[x]=i;
      x=tmp;
}
return i;
}



void Union(int R1,int R2)   //将两个不同集合的元素进行合并,使两个集合中任两个元素都连通
{
int tmp=parent[R1]+parent[R2];
if(parent[R1]>parent[R2]) //优化方案――加权法则
{
   parent[R1]=R2;
   parent[R2]=tmp;
}
else
{
   parent[R2]=R1;
   parent[R1]=tmp;
}
}

在Find函数中如果仅仅靠一个循环来直接得到节点的所属集合根结点的话。通过多次的Union操作就会有很多节点在树的比较深层次中,再Find起来就会很费时。通过加一个While循环(压缩路径)每次都把从i到集合根结点的路过结点的双亲直接设为集合的根结点。虽然这增加了时间,但以后的Find会快。平均效能而言,这是个高效方法。




两个集合并时,任一方可做为另一方的孩子。怎样来处理呢,现在一般采用加权合并,把两个集合中元素个数少的做为个数多的孩子。有什么优势呢?直观上看,可以减少集合树的深层元素的个数,减少Find时间。

如从0开始到N不断合并i和i+1结点会怎样呢?




这样Find任一节点所属集合的时间复杂度几乎都是O(1)!!

不用加权规则就会得到




这就是典型的退化树现象,再Find起来就会很费时(如找N-1节点看看)。

以下是读入等价对后的parent[N]查找合并过程状态:







再说一个并查集应用最完美的地方:最小生成树的kruskal算法:

算法基本思想是:

开始把所有节点作为各自的一个单独集合,以为每次从边信息中得到一个最短的边,如果这个边邻接了两个不同集合的节点,就把这两个节点的所属集合结合起来,否则继续搜索。直至所有节点都同属一个集合,就生成了一个最小生成树。int kruskal(int parent[],int N)
{
int i,j,k,x,y;
int min;
while(k<=N-1) //产生N-1条边
{
   min=MAX_INT;
   for(i=1;i<=N;i++)
    for(j=1;j<=N;j++)
    {
     if(sign[i][j]==0&&i!=j) //sign[N][N]是标志节点是否被访问过或已被排除……
     {
      if(arcs[i][j]<min) //arcs[N][N]存放边的长度
      {
       min=arcs[i][j];
       x=i;
       y=j;
      }//if
     }//if
    }//for
   if(Find(parent,x)==Find(parent,y)) //如X,Y已经属同一连通分量,则不合并,排除……
    sign[x][y]=1;
   else
    {
     Union(parent,x,y);
     Sign[x][y]=1;
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值