使用STL实现并查集

我以最入门的并查集水题——宗教信仰为例,来演示使用STL里的multimap(多重映照容器)模拟并查集数据结构(并查集应该也可以用其他STL实现,这里我仅以multimap为例)。
Multimap和map类似,不过可以插入重复键值,都要包含头文件,因为multimap可以插入重复键值,所以其搜索(find)、插入(insert)、删除(erase)都与map不一样。
先简单描述一下题目的大意:
在你的大学里有n个学生,因为直接问其宗教信仰挺不合适的,所以想个办法,就是一次问两个学生,问他们是否信仰同一个宗教。从这个数据你并不能直接得出一共有几种不同的宗教,但是你可以根据他们之间是否在一个宗教集合来确定,一共有几个不同的集合,最后得到宗教数目。PS:假设每个同学只信仰一个宗教。
输入:开头包含两个整数m和n,接下来有m行,每行有两个整数i和j,代表i同学和j同学是同属一个宗教信仰。学生编号从1到n,输入以0 0结尾。
输出:每次的最多的宗教数目。
 
解题思路:
非常基础的并查集,可以先让每个同学初始化为每个人有一个宗教信仰,也就是将一棵树构建出一个父节点,一旦有某两个同学是同属一个宗教信仰,就将后者的树并到前者上,这样就会在同一棵树下,也就是同一集合内。
正好利用multimap的多重映照关系来实现并查集。
首先我们先建立一个容器: multimap m;
随后把n个学生建立为n个树构成的森林,每棵树有一个根节点表示自己。
用insert方法:
for( int i = 1 ; i <= n ; i++ )
M.insert( pair(i,i) );
下面来解决把两个人并到同一个集合的过程:
首先,我们创建两个多重映照容器的iterator迭代器j,k:
Multimap::iterator  j,k;
然后,我们用j指向前面的那个童鞋a,用k指向后面的那个童鞋b(a,b为输入的同学的编号):
j = m.find(a);
k = m.find(b);
如果二者的键值不同(也就是父节点不同),就将b并到a,并且删除b树:
if( j->first != k->first )
{
m.insert(pair(a,b));
m.erase(b);
}
重复这个过程,最终互不相同键值数目就是所得到的宗教信仰数目
我的源代码:
#include
#include
#include
using namespace std;
multimap m;
int num,n;
int main()
{
    int a,b,sum;
    while(scanf("%d%d",&num,&n)!=EOF , num&&n)
    {
        sum = 0;
        for( int i = 1 ; i <= num ; i++ )
            m.insert(pair(i,i) ); 
    multimap::iterator j,k;
        for( int i = 1; i <= n ; i++ )
        {
            scanf("%d%d",&a,&b);
            j = m.find(a);
            k = m.find(b);
            if( j->first != k->first )
            {
                m.insert(pair(a,b));
                m.erase(b);
            }
        }
//下面是查找不同键值的数目,最终输出结果并清空multimap容器
multimap::iterator i,ti;
        for( i = ti = m.begin(); ti != m.end() ; ti++)
        {
            if(ti == m.begin())sum = 1;
            if( ti->first != i->first )
            {
            i = ti;
            sum++;
            }
        }
        cout< m.clear();
    }
    return 0;
}

你可能感兴趣的:(STL)