并查集算法

/*并查集的基本应用——POJ1611*/

/************************************************************************
大致题意:一共有n个学生(编号0 至 n-1),m个组,一个学生可以同时加入不同的组。
现在有一种传染病,如果一个学生被感染,那么和他同组的学生都会被感染。现在已
知0号学生被感染,问一共有多少个人被感染。


首先将每个学生都初始化为一个集合,然后将同组的学生合并,设置一个数组num[]
来记录每个集合中元素的个数,最后只要输出0号学生所在集合中元素的个数即可。

*************************************************************************
*/

#include <iostream>
using namespace std;
//num[i]数组存储节点所在的集合总数
//father[]数组存储点

int num[30001],rank[30001],father[501];
void makeSet(int x){
    father[x] = x;//初始化连通分量,每个点为一个连通分量
    rank[x] = 0;
    num[x] = 1;//每个连通分量里面只有一个元素
}

//查找X点所在树中的根节点
int findSet(int x){
    int r,j;
    r = x;
    //返回X所在树的根节点
    if(father[r] != r)
        r = father[r];
    //压缩路径
    while(x != r){
        j = father[x];
        father[x] = r;
        x = j;
    }
    return x;
}
//用根节点来代替集合
void merge(int a,int b){
    int x,y;
    // 查找出a,b所在树中的根节点
    x = findSet(a);
    y = findSet(b);
    if(a == b) return;//
    //如果x,y不在同一个树中,则合并这两个集合
    //同时,为了平衡树,我们将节点少的加入到节点多的里面去
    if(x != y){
        if(rank[x] == rank[y]){
            rank[x]++;
            father[y] = x;//将节点数目少的集合的根改成x;
            num[y] += num[x];//将修改集合y的节点数目
        }else if(rank[x] < rank[y]){
            father[x] = y;
            num[y] += num[x];
        }
        else{
            father[y] = x;
            num[x] += num[y];
        }
    }
}

int main(){
    int N,M;
    int i,j,k;
    //输入N,M
    while(cin>>N>>M){
        if(M==N && N == 0) break;
        if(M == 0)
        {
            cout << "1\n"; continue;
        }
        //初始化个连通分量
        for(i = 0; i < N; i++) makeSet(i);
        int t,first,next;
        for(i = 0; i < M; i++){
            cin>>t>>first;
            for(j = 1; j < t; j++){
                cin>>next;
                merge(first,next);//将输入的节点合并成一个集合
            }
        }
        k=findSet(0);
        //题目要求的是:student 0所在集合中节点的数目
        //return ;
        cout<<num[k]<<endl;
    }
    return 0;
}

你可能感兴趣的:(并查集算法)