简、易-sachinKung

导航

并查集(一)理解

并查集(union-¯nd set) 维护一些不相交的集合,它是一个集合的集合。每个元素

恰好属于一个集合,好比每条鱼装在一个鱼缸里。每个集合S有一个元素作为\集合代

表"rep[S],好比每个鱼缸选出一条"鱼王"。并查集提供三种操作:

MakeSet(x):建立一个新集合xx应该不在现有的任何一个集合中出现。

Find(S, x):返回x所在集合的代表元素。

Union(x, y):把x所在的集合和y所在的集合合并。

森林表示法

可以用一棵森林表示并查集,森林里的每棵树表示一个集合,树根就

是集合的代表元素。一个集合还可以用很多种树表示,只要树中的结点不变,表示的

都是同一个集合。

合并操作

只需要将一棵树的根设为另一棵即可。这一步显然是常数的。一个优化

是:把小的树合并到大树中,这样会让深度不太大。这个优化称为启发式合并。

查找操作

只需要不断的找父亲,根就是集合代表。一个优化是把沿途上所有结点

的父亲改成根。这一步是顺便的,不增加时间复杂度,却使得今后的操作比较快。这个优化称为路径压缩,

实现代码

用p[i] 表示i 的父亲,而rank[i] 表示i 的秩,并用秩来

代替深度做刚才提到的启发式合并。

void makeset(int x)

{

    p[x]=x;

    rank[x]=0;

}

int findset(int x)

{

    if(x!=p[x])

    p[x]=findset(p[x]);

    return p[x];

}

//////   使用非递归的更好理解

int findset ( int x)
{
int px = x , i;
while (px != p[px ]) px = p[px ]; // find root
while (x != px ) // path compression
{
i = p[x];
p[x ] = px;
x = i;
}
return px;
}

void union_set(int x,int y)

{

    x=findset(x);

    y=findset(y);

    if(rank[x]>rank[y])

    p[y]=x;

    else

        {

            p[x]=y;

            if(rank[x]==rank[y])rank[y]++;

}

}

posted on 2012-09-18 21:32  sachinKung  阅读(164)  评论(0编辑  收藏  举报