超详细的并查集讲解 首发


/**
并查集呢,我想着以HDU1232 畅通工程为例子细讲;
这个题目应该很简单吧,就是求存在多少个连通分量;
并查集是一个什么东西,它主体包括三个东西:pre[i]数组,find函数,join函数;
那么这三个东西是什么鬼呢,分别表示的是 i的父节点称前导点,查找父节点的操作并进行优化的操作,看是否需要添加另一层关系添加新的前导点;
那么有了这三个部分之后我们可以干嘛呢
说一下它的功能吧:判断两个点是否具有连通性,判环(克鲁斯卡尔最小生成树);
但是呢  这个东西感觉和KMP的next数组,前向星,领接表有点类似;没什么  没什么;
*/
#include
using namespace std;

int ans,pre[1000];

int  Find(int root)
{
    int son=root,temp;

    /**
    因为我们开始的初始化操作都是自己做自己的掌门,所以在所有的节点中一定存在自己是自己的掌门,就是这个人很特别,走到了尽头;
    它自己永远还是自己,所以说查找的时候当自己的父节点为自己的时候那么自己就独成一派了,说白了就是自己是老大;
    */
    while(root!=pre[root])
        root=pre[root];

    /**l路径压缩,将这条路上所有指向同一父节点的节点直接指向父节点;
    形象的讲就是:a->b->c->d,化简为a->d,b->d,c->d;
    这样的话下次查找的时候就不需要一个一个查找过去了,可以直接得到自己的掌门;
    这个个人感觉就是并查集最灵魂的最骚的操作了;
    */
    while(son!=root)
    {
        temp=pre[son];
        pre[son]=root;
        son=temp;
    }
    return root;

    /**那么为什么会有不同的门派呢,因为我们对每输入两个点会进行合并操作,就是将两个门派进行合并;
    就是说一旦他们的门派(唯一)不一样,两派自成一派(随便打架,让他们形成pre[x]=y,就是随便一个人当另一个人的老大);
    那么这里还有合并操作;
    */
}

void join(int root1,int root2)
{
    int x=Find(root1),y=Find(root2);///如果说所查找的父节点不一样,那么我们就有必要去形成新的前导和后继的关系;
    if(x!=y) { pre[x]=y;ans--;}
}

int main ()
{
    int n,m;
    while(~scanf("%d",&n)&&n)
    {
        scanf("%d",&m);
        ans=n-1;///最原始的连接只有n-1条边;
        for(int i=1;i<=n;i++)
            pre[i]=i;///开始的时候我们都认自己为父节点,自己是自己的父亲;自成一派;
        for(int i=1;i<=m;i++)
        {
            int x,y;
            scanf("%d %d",&x,&y);
            join(x,y);
        }
        printf("%d\n",ans);
    }
    return 0;
}




你可能感兴趣的:(ACM--并查集,HDU)