cpp-并查集

并查集

  1. 定义

定义引用百度百科:

并查集,在一些有N个元素的集合应用问题中,我们通常是在开始时让每个元素构成一个单元素的集合,然后按一定顺序将属于同一组的元素所在的集合合并,其间要反复查找一个元素在哪个集合中。这一类问题近几年来反复出现在信息学的国际国内赛题中,其特点是看似并不复杂,但数据量极大,若用正常的数据结构来描述的话,往往在空间上过大,计算机无法承受;即使在空间上勉强通过,运行的时间复杂度也极高,根本就不可能在比赛规定的运行时间(1~3秒)内计算出试题需要的结果,只能用并查集来描述。

简单的说将数据按共有的特性分类,每一个分类为一个树。一个个分类树组成的森林就是并查集,拥有合并(merge)操作和查找操作(find),合并是将新的数据加入到并查集,查找是找到指定数据的根节点。

并查集的一般用途就是用来维护某种具有自反、对称、传递性质的关系的等价类。

  1. 数据结构
    简单的并查集运用一个数组来存储每个数据的父节点,初始值为-1或者数组下标的时候表示该节点为根节点。
vector<int>parent

例如:

parentValue
0-1
1-1
2-1
parentValue
00
11
22

如果用-1来表示该节点为根节点,需要在合并的时候判断树根节点是否相同,不然会在find函数中出现死循环。
例如: 3的根节点为0,而6的根节点也是0
合并后会出现0的根节点为0(应该为-1),然而在find函数中递归寻找0的根节点,直到找到的parent值为-1才停下,这样程序会一直在find函数中循环。
如果用i来初始化,0的根节点为0,表示0自己为根,find递归会直接结束。
(注:合并的实质是将根节点成为另一个树根节点的子节点)

  1. 具体实现
 class ufs {
     public:
         ufs(int n) :
         parent(vector<int>(n, -1)), 
         count(0) 
         {
     			//初始化一个parent数组全部值为0
     			//也可以用i初始化
     			//for(int i=0;i<n;i++)
     			//	parent[i]=i
         }
		//查找根节点用于merge函数合并
         int find(int value) {
             if (parent[value] == -1) {
                 return value;
             }

             return find(parent[value]);
         }

         void merge(int x, int y) {
             int find_x = find(x);//找到x的根节点
             int find_y = find(y);//找到y的根节点
             //避免根节点相同的情况  find死循环
             //如果使用i来初始化就不用这一步 
             if(find_x!=find_y)
             parent[find_y] = find_x;
         }

         vector<int>parent;
     };
  1. 优化
    设立一个rank数组用来记录树的深度,每一次合并的时候都是深度较小的树成为深度较大的树的子节点。
    优化后的代码:

     class ufs {
     public:
         ufs(int n) :parent(vector<int>(n, -1)), count(0),rank(vector<int>(n)) {
       /*      for (int i = 0; i < n; i++)
                 parent[i] = i;*/
         }

         int find(int value) {
             if (parent[value] == -1) {
                 return value;
             }
             
             return parent[value]=find(parent[value]);
         }

         void _union(int x, int y) {
             int find_x = find(x);
             int find_y = find(y);
             if(find_x!=find_y){
                if(rank[x]>rank[y]){
                    swap(find_x,find_y);//比较深度
                }
                parent[find_y] = find_x;//y对应的根节点成为x对应根节点的子节点
                
                if(rank[x]==rank[y]){
                    rank[x]++;//如果深度相同,合并后x的深度会+1
                }
             }
         }
         
         vector<int>parent;
         vector<int>rank;//记录深度
     };
 };
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值