找网上的面经的时候,看到的一个题目:
阿里旺旺里人和人之间交流产生了很多个商业群,找出一共有多少个不同的群。
给出的答案是并查集。才疏学浅,不知道并查集是什么。。
以下给出一个并查集的列子,可能就会比较好理解:
初始状态,每个元素为单调一个集合;
(2,4) -> {2,4}
(5,7) -> {2,4} {5,7}
(1,3) -> {1,3} {2,4} {5,7}
(8,9) -> {1,3} {2,4} {5,7} {8,9}
(1,2) -> {1,2,3,4} {5,7} {8,9}
(5,6) -> {1,2,3,4} {5,6,7} {8,9}
(2,3) -> {1,2,3,4} {5,6,7} {8,9}
最后我们得到3个集合{1,2,3,4}, {5,6,7}, {8,9},于是判断两个人是否有关,就变成判断两个数是否在同一个集合中的问题。如此一来,需要的数据结构就没有图结构那样庞大了。
算法的几个子过程:
(1) 开始时,为每个人建立一个集合SUB-Make-Set(x);
(2) 得到一个关系后a,b,合并相应集合SUB-Union(a,b);
(3) 此外我们还需要判断两个人是否在同一个集合中,这就涉及到如何标识集合的问题。我们可以在每个集合中选一个代表标识集合,因此我们需要一个子过程给出每个集合的代表元SUB-Find-Set(a)。于是判断两个人是否在同一个集合中,即两个人是否为亲戚,等价于判断SUB-Find-Set(a)=SUB-Find-Set(b)。
优化方法为路径压缩,此时的算法复杂度为O(1) (平均)
每次查找的时候,如果路径较长,则修改信息,以便下次查找的时候速度更快。
实现:
- 第一步,找到根结点。
- 第二步,修改查找路径上的所有节点,将它们都指向根结点。
find3(x)
{
r = x; while (set[r] <> r) //循环结束,则找到根节点
r = set[r];
i = x;
while (i <> r) //本循环修改查找路径中所有节点
{
j = set[i];
set[i] = r;
i = j;
} }
CSDN:http://www.cnblogs.com/cyjb/p/UnionFindSets.html
这个说的也蛮清楚的。