并查集压缩路径

1.引入什么是并查集?

导引问题:【犯罪团伙】

    警察抓到了n个罪犯,警察根据经验知道他们属于不同的犯罪团伙,却不能判断有多少个团伙,但通过警察的审讯,知道其中的一些罪犯之间相互认识,已知同一犯罪团伙的成员之间直接或间接认识。有可能一个犯罪团伙只有一个人。

    请你根据已知罪犯之间的关系,确定犯罪团伙的数量。已知罪犯的编号从1至n。

输入:

第一行:n(<=10000,罪犯数量),

第二行:m(<=100000,关系数量)

以下若干行:每行两个数:I和j,中间一个空格隔开,表示罪犯i和罪犯j相互认识。

输出:一个整数,犯罪团伙的数量。

 

2.抽象的算法:

◆  开始把n个人看成n个独立集合。

◆每读入两个有联系的人i和j,查找i和j所在的集合p和q,如果p和q是同一个集合,不作处理;如果p和q属于不同的集合,则合并p和q为一个集合。

◆最后统计集合的个数即可得到问题的解。

 3.什么是并查集?

◆并查集是一种树型的数据结构,用于处理一些不相交集合S={S1, S2,…,Sn},每个集合Si都有一个特殊元素root[Si],称为集合的代表元.

◆并查集支持三种操作:

Init(X):集合初始化:把元素xi加到集合Si中。每个集合Si只有一个独立的元素xi,并且元素xi就是集合Si的代表元素。father[xi]=-1(或者xi);  每个结点都是一颗独立的树,-1是该树的代表元素。

                  void Initial(int father[]){


                          for(int i=0;i<size;i++)


                              father[i]=-1;


                   }     

                      

Find(x):查找:查找xi所在集合Si的代表root[Si]。

               优化:路径压缩。查找出对应得根节点

            int Find(int father[],int x){


                            while(father[x]>=0)


                                         x=father[x];


                            return x;


             }

 

Union(int father[],x,y): 合并:把x和y所在的两个不同集合合并。

         void  Union(int father[],int root1,int root2){


                            father[root2]=root1;//将根root2连接到跟root1下面


          }

 4.并查集的压缩

下面是采用路径压缩的方法查找元素:

int find(int x)       //查找x元素所在的集合,回溯时压缩路径
{
    
    
       return father[x]==-1?x: father[x] = find(father[x]);     //回溯时的压缩路径
            //从x结点搜索到祖先结点所经过的结点都指向该祖先结点
    
}
   

    上面是一采用递归的方式压缩路径, 但是,递归压缩路径可能会造成溢出栈,下面我们说一下非递归方式进行的路径压缩:(即先找到父节点,再将路径上的所有节点的父节点都设为根节点。)

int find(int x)
{
    int k, j, r;
    r = x;


    while(father[r]>=0)     //查找跟节点
        r = father[r];      //找到跟节点,用r记录下
    k = x;        


    while(k != r)             //非递归路径压缩操作
    {
        j = parent[k];         //用j暂存parent[k]的父节点
        parent[k] = r;        //parent[x]指向跟节点
        k = j;                    //k移到父节点
    }
    return r;         //返回根节点的值            
}
 

例题:两个人一组,输入人的组数n,再输入n组人。每组的两个人表示这两个人是好朋友,a得好朋友是b,b得好朋友是c,则c也是a得好朋友。求最大的好朋友团体的人数,并输出。

#include <iostream>
#include <cstdio>
using namespace std;
#define N 10000005
int p[N];
int sum[N];


int Find_set(int x) {//采用了路径压缩
    return p[x]==-1 ? x:p[x]=Find_set(p[x]);
}
int main() {
    int n;
    while (scanf("%d", &n) != EOF) {
        memset(p, -1, sizeof(p));
        for (int i=1; i<=N; i++)
            sum[i] = 1;
        while (n --) {
            int a, b;
            scanf("%d%d", &a, &b);
            a = Find_set(a);
            b = Find_set(b);
            if (a != b) {
                p[a] = b;
                sum[b] += sum[a];
            }
        }
        int ans = 1;
        for (int i=1; i<=N; i++) {
            if (p[i]==-1 && sum[i]>ans)
                ans = sum[i];
        }
        printf("%d\n", ans);
    }
}
    原文作者:犯罪团伙问题
    原文地址: https://blog.csdn.net/woshinannan741/article/details/50224253
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞