并查集

 

将多个集合合并成没有交集的集合。
给定一个字符串的集合,格式如:{aaa bbb ccc}, {bbb ddd},{eee fff},{ggg},{ddd hhh}要求将其中交集不为空的集合合并,要求合并完成后的集合之间无交集,例如上例应输出{aaa bbb ccc ddd hhh},{eee fff}, {ggg}。
(1)请描述你解决这个问题的思路;
(2)请给出主要的处理流程,算法,以及算法的复杂度
(3)请描述可能的改进。

2、分析

1. 假定每个集合编号为0,1,2,3...
    2. 创建一个hash_map,key为字符串,value为一个链表,链表节点为字符串所在集合的编号。遍历所有的集合,将字符串和对应的集合编号插入到hash_map中去。
    3. 创建一个长度等于集合个数的int数组,表示集合间的合并关系。例如,下标为5的元素值为3,表示将下标为5的集合合并到下标为3的集合中去。开始时将所有值都初始化为-1,表示集合间没有互相合并。在集合合并的过程中,我们将所有的字符串都合并到编号较小的集合中去。
    遍历第二步中生成的hash_map,对于每个value中的链表,首先找到最小的集合编号(有些集合已经被合并过,需要顺着合并关系数组找到合并后的集合编号),然后将链表中所有编号的集合都合并到编号最小的集合中(通过更改合并关系数组)。
    4.现在合并关系数组中值为-1的集合即为最终的集合,它的元素来源于所有直接或间接指向它的集合。
    0: {aaa bbb ccc}
    1: {bbb ddd}
    2: {eee fff}
    3: {ggg}
    4: {ddd hhh}
    生成的hash_map,和处理完每个值后的合并关系数组分别为
    aaa: 0           
    bbb: 0, 1        
    ccc: 0          
    ddd: 1, 4       
    eee: 2           
    fff: 2          
    ggg: 3           
    hhh: 4          
    所以合并完后有三个集合,第0,1,4个集合合并到了一起,
    第2,3个集合没有进行合并。

例题:现在有n个学生,属于m个group,学生编号为0~n-1,学生0被认为是一个suspect,和学生0在一个group的学生都被认为是suspects,求出suspects的值。以下输入表示:

100表示:100个学生;4表示:4个group;下面每一行表示一个group,第一个数字表示group中学生数目,接着是具体的学生编号。

Sample Input

100 4
2 1 2
5 10 13 11 12 14
2 0 1
2 99 2
200 2
1 5
5 1 2 3 4 5
1 0
0 0

Code
 #include<iostream>
 using namespace std;
 int n, m, i, j;
 int father[30005], num[30005];
 void makeSet(int n)
 {
   for(i = 0; i < n; i++)
   {
       father[i] = i; //使用本身做根
       num[i] = 1;
   }
}
int findSet(int x)
{
   if(father[x] != x) //合并后的树的根是不变的
   {   
       father[x] = findSet(father[x]);
   }
   return father[x];
}
void Union(int a, int b)
{
   int x = findSet(a);
   int y = findSet(b);
   if(x == y)
   {
       return;
   }
   if(num[x] <= num[y])
   {
       father[x] = y;
       num[y] += num[x];
   }
   else
   {
       father[y] = x;
       num[x] += num[y];
   }
}
int main()
{
   while(scanf("%d %d", &n, &m)!=EOF && n != 0)
   {
       makeSet(n);
       for(i = 0; i < m; i++)
       {
           int count, first, b;
           scanf("%d %d",&count, &first);
           for(j = 1; j < count; j++)
           {
               scanf("%d",&b);
               Union(first,b);
           }
       }
       printf("%d\n",num[findSet(0)]);
   }
   return 0;
}

posted @ 2013-08-19 14:59  sandyhit  阅读(212)  评论(0编辑  收藏  举报