您的位置:首页 > 其它

并查集 (Disjoint Set)

2016-04-19 16:45 218 查看
本文链接:http://i.cnblogs.com/EditPosts.aspx?postid=5408816

[b]问题:[/b]

  在某个城市里,住着N个人,这N个人都有自己的公司,现给出N个人的M条信息(即某两个人属于同一个公司),问这个城市最多有多少个公司。

[b]THINK:[/b]

比如给出

10 6

1 3

3 7

2 5

5 9

9 10

6 8

  代表有10个人,6个关系,1 和 3 在一个公司里, 3 和 7 在一个公司里,2 和 5 在一个公司里, 5 和 9 在一个公司里,9 和 10 在一个公司里, 6 和 8 在一个公司里。那么1 3 7则在一个公司里,2 5 9 10在另外一个公司里,4 单独在一个公司里,6 和8 在一个公司里,则这个城市最多有 4 个公司。

  把这个问题数学化,即有N个元素,这N个元素都属于某一个集合,给出M个关系,代表某两个元素在一个集合里,问最多有多少个集合,今天讨论的并查集算法可以很好的解决这个问题。

[b]并查集(Disjoint Set):[/b]

  把这N个元素初始化为N个不相交集合,在每个集合里面选择其中某个元素代表所在集合的名字。

  常见操作:

  1):合两个集合;

  2):找某元素所在集合

  即“并查集”。

  具体实现:

  用编号最小的元素的标记某个集合。

  定义一个数组pre[1...N],其中pre[i]代表i所在集合。

  


  则最后的集合为:{1, 3, 7},{2, 5, 9, 10},{4}, {6, 8},第一个集合代号为 1 ,第二个集合代号为 2 ,第三个集合代号为 4 ,第四个集合代号为 6。

初始化代码:

void initPre()
{
for(int i = 1; i <= N; ++i)
pre[i] = i;
}


查找代码:

int Find(int x)
{
int r = x;
while (pre[r] != r)
r = pre[r];
return r;
}


合并代码:

void mix(int x, int y)
{
int fx = Find(x);
int fy = Find(y);
if(fx < fy)
pre[fy] = fx;
if(fx > fy)
pre[fx] = fy;
}


  初始化代码比较容易理解,下面来解释查找代码。拿上面第二个集合来说吧,比如查找 10 在哪个集合里面,前面说过,pre[i] 代表 i 所在集合的编号,那么看pre[10],因为pre[10] 等于 9 那么 10 在 代号为 “9” 的集合里面吗?这个显然不是,因为上面根本不存在代号为 “9” 的集合,这是为什么?之前说过用这个集合里面元素最小的数字代表这个集合的编号,则代表这个集合编号的元素的 pre[i] 一定等于 i,由于pre[9] != 9,所以继续往上找,找到了5,pre[5] != 5,那么继续向上找到 1 ,pre[1] = 1,即找到了代表 10 所在集合的编号为 “1”,这就是查找操作。

  再来解释合并操作,比如我们要合并 8 和 10,我们先找到 8 所在集合的编号为 “6”,10 所造集合的编号为 “1”,由于 10 所在集合的编号小于 8 所在集合的编号,于是就可以让 8 所在集合的编号 “6” 改成 10 所在集合的编号 “1”,即 pre[6] = 1,于是就完成了合并操作。

  到了这里你有没有觉得有什么不妥的地方呢?比如再次查找 10 所在的集合,是不是又得一步一步的向上查找?的确是的,可是刚才已经查找到了 10 所在的集合编号为 “1”,为什么还有查找呢?由于并没有改变 pre[10] 的值,那么每次查找都需要做这样的动作。所以当查找到10所在的集合编号为 “1” 时可以直接让pre[10] = 1。那么就这么结束了吗,还没有,由于在查找 10 的过程中还查找了 9 ,那么顺便还可以让pre[9] = 1,于是下次查询时就很省时间了,这就是所谓的路径压缩。

  (推荐一个详解路径压缩的博客:http://blog.csdn.net/niushuai666/article/details/6662911)

含路径压缩的查找代码:

int Find(int x)
{
int r = x;
while(r != pre[r])
r = pre[r];
int i = x, j;
while(pre[i]!=r)
{
j = pre[i];
pre[i] = r;
i = j;
}
return r;
}


[b]递归压行式:[/b]

int Find(int x){ return x == pre[x] ? x : pre[x] = Find(pre[x]); }
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: