并查集

简介

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

  并查集是一种树型的数据结构,用于处理一些不相交集合(Disjoint Sets)的合并及查询问题。常常在使用中以森林来表示。(转自百度百科)

主要操作

  众所周知,每个学校都有些奇怪的组织,有些是学校的,还有些''地下党''(咳咳,懂得),然后每个组织又分为了几个小组........当每年开学的那一天。总有些新面孔会来到这些组织,但是显然暂时不会。,这就意味着他们不属于任何一个组织

初始化

for(int i=1;i<=n;i++)f[i]=i;

  几个月后,这些新同学陆陆续续的加入了组织,有一天,学校让所有的组织集合,这下好了,因为是新同学,所以他们只记得自己的上级,所以只能去找上级,好巧不巧,上级也只认识自己的上级(????)最后一大堆人浩浩荡荡地找到了组织长,愉快地集合了起来

查找

int get(int x)
{if(f[x]==x)return x;//找到根节点 return get(f[x]);//不然就继续向上找
}

然而这些人的智商也不算低,记住了自己的组织长,下次集合直接找自己的组织长就可以了,不用麻烦的走一趟了。

路径压缩

int get(int x)
{if(f[x]==x)return x;//找到根节点 return f[x]=get(f[x]);//不然就继续向上找,然后记录
}

  然后,学校嫌组织过多,要合并一些组织了,然后把XX部和OO部合并在了一起,但是一个部得有一个领头人啊,所以,我们决定让XX部的部长当了新的XO部的部长。这样OO部部长的上级就是XX部部长了(OO部部长:为什么不是我)

合并

void merge(int x,int y)
{int tx=get(x);int ty=get(y);if(tx!=ty)//不在同一集合就合并(其实也可以不要判断,反正在同一集合合并了也不会变) fa[tx]=ty;return;
}

实际应用

P3367 【模板】并查集

嗯,这就是一道模板题

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 int n,m;
 4 int fa[10001];
 5 int get(int x)//找根结点
 6 {
 7     if(fa[x]==x)return x;
 8     else return fa[x]=get(fa[x]);//路径压缩
 9 }
10 void merge(int x,int y)//合并
11 {
12     int tx=get(x),ty=get(y);
13     if(tx!=ty)
14         fa[tx]=ty;
15     return;
16 }
17 int main()
18 {
19     scanf("%d%d",&n,&m);
20     for(int i=1;i<=n;i++)
21         fa[i]=i;//初始化
22     while(m--)
23     {
24         int z,x,y;
25         scanf("%d%d%d",&z,&x,&y);
26         if(z==1)
27             merge(x,y);
28         else
29         {
30             if(get(x)==get(y))cout<<"Y"<<endl;
31             else cout<<"N"<<endl;
32         }
33     }
34     return 0;
35 }

带权并查集

  嗯,和普通的并查集没什么区别

  普通的并查集代表着集合与集合之间的关系,但是带权并查集还维护了点与点之间的关系(毕竟带权嘛)。但是上例题前,我们先来康康怎么来带权并查集的路径压缩。

查找

首先,这里有一个未压缩的并查集,假设查找E的根结点,我们需要E到D的距离加上D到A的距离,但是D到A的距离,又需要D到B的距离加上B到A的距离.....

sum表示这个点到根结点的权值

1 int get(int x)
2 {
3     if(root[x]==x)return x;
4     int fa=get(root[x]);
5     sum[x]+=sum[root[x]];
6     return root[x]=fa;
7 }

注意第四行,一定要写在压缩权值的前面,以图为例,如果写在后面,那么E到A的距离会变成ED加上DB,就是E到B的距离了,然而这不是我们想要的结果,所以先找到根,然后从根结点慢慢回来,路上再更新权值,这样才是有效的。而不是从某个结点向根结点更新。

合并

首先你有两个关系

然后题目又给了你x到y的权值,现在有了这一条红线你就要合并这两个并查集了

我们想把tx并到ty去,但是就必须得知道tx到ty的权值

我们不难知道,x到ty的两条线路的权值都得相同,所以只需要用v2+s-v1即可

root[tx]=ty;
sum[tx]=s+sum[y]-sum[x];

HDU—3008

嗯,看看这个题,大概就是有一串你不知道的数,每次给你一个区间的和,看是否与前面已知的重复。例如已知[2,9]=10,[2,5]=8,又给你[6,9]=3,那么显然不对,因为我们从前面可知[3,9]应该等于2(自己算的出来吧)。

  但是还有一个注意的地方,我们用sum数组代表一个点到根结点的所有权值,但是初始化的时候例如sum[1]=0(此时1本身是根结点),这个代表的是[1,1]等于0,也就是a[1]到a[1]等于0.但是这样的话就表示你已经确切的知道了这个值,然而我们不知道这个值,所以不妨把闭区间的某一段变成开区间,例如[1,1]改成(0,1],这样sum[1]就代表(1,1],也不会冲突了。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 int n,m,ans;
 4 int root[200005],sum[200005];
 5 int get(int x)//查找
 6 {
 7     if(root[x]==x)return x;
 8     int fa=get(root[x]);
 9     sum[x]+=sum[root[x]];
10     return root[x]=fa;
11 }
12 int main()
13 {
14     while(~scanf("%d%d",&n,&m))
15     {
16         ans=0;
17         for(int i=0;i<=n;i++)//初始化,一定要从0开始
18         {
19             root[i]=i;
20             sum[i]=0;
21         }
22         while(m--)
23         {
24             int x,y,s;
25             scanf("%d%d%d",&x,&y,&s);
26             x--;
27             int tx=get(x);
28             int ty=get(y);
29             if(tx==ty)//如果是同一个并查集,就看是否合法即可
30             {
31                 if(sum[x]-sum[y]!=s)
32                     ans++;
33             }
34             else//否则合并一下
35             {
36                 root[tx]=ty;
37                 sum[tx]=s+sum[y]-sum[x];
38             }
39         }
40         cout<<ans;
41     }
42     return 0;
43 }

种类并查集

POJ-1182

种类并查集,就像他的名字一样,有几个种类,就有几个区间,对应的就要开几倍数组。所谓区间,就像下面的图一样:

我们现在假设有两个种类,然后每一个数都有可能是x种类或者y种类,随之对应的,如果一个数m是x种类,那么它就是m,否则它是m+n

.再来看看例题,它有三个种类,所以我们要开三倍数组,然后首先每个数的根结点都是自己,我们现有三个种类:A,B,C,然后得到了a吃b,a有三种情况,当a为A种类时,b就为B种类,当a为B种类时,b就为C种类.......然后三个并查集分别维护,一旦发生冲突,就ans++即可

 1 #include<iostream>
 2 using namespace std;
 3 int root[500005];
 4 int n,m,ans;
 5 int get(int x)//查找
 6 {
 7     if(root[x]==x)return x;
 8     return root[x]=get(root[x]);
 9 }
10 void merge(int x,int y)//合并
11 {
12     int tx=get(x);
13     int ty=get(y);
14     root[tx]=ty;
15     return;
16 }
17 int main()
18 {
19     scanf("%d%d",&n,&m);
20     for(int i=1;i<=3*n;i++)//初始化,三倍数组
21         root[i]=i;
22     while(m--)
23     {
24         int x,y,z;
25         scanf("%d%d%d",&z,&x,&y);
26         if(x>n||y>n||z==2&&x==y)ans++;//不合法
27         else if(z==1)//同一种类
28         {
29             if(get(x+n)==get(y)||get(x+2*n)==get(y))//如果他们已经有了关系并且不是同一种类,ans++
30             {
31                 ans++;
32                 continue;
33             }
34             merge(x,y);//都是A种
35             merge(x+n,y+n);//都是B种
36             merge(x+2*n,y+2*n);//都是C种
37         }
38         else//x吃y
39         {
40             if(get(x)==get(y)||get(x+n)==get(y))//如果他们有了关系并且是同种类或者x被y吃,ans++
41             {
42                 ans++;
43                 continue;
44             }
45             merge(x,y+n);//x是A种,吃为B种的y
46             merge(x+n,y+2*n);//x是B种,吃为C种的y
47             merge(x+2*n,y);//x是C种,吃为A种的y
48         }
49     }
50     cout<<ans;
51 }

然后这道题也可以用权值并查集来做,我们假设同根结点比,0是同类,1被根结点吃,2吃根结点,然后初始化是每个点都是自己的同类,和权值并查集的合并一样.但是因为这里的关系只有三种,所以当我们维护权值的时候有一个取模三的操作。如图,tx与ty的关系就是(1+0+3-2)%3=2.(然而我好像没有这样写)

 1 #include<iostream>
 2 using namespace std;
 3 int n,m,ans;
 4 struct node{
 5     int root;
 6     int val;//0同类,1被吃,2吃根
 7 }a[50005];
 8 int get(int x)
 9 {
10     if(a[x].root==x)return x;
11     int y=a[x].root,z=get(y);
12     a[x].val=(a[x].val+a[y].val)%3;
13     return a[x].root=z;
14 }
15 int merge(int z,int x,int y)
16 {
17     int tx=get(x),ty=get(y);
18     if(tx==ty)//在同一集合
19     {
20         if((a[y].val+(3-a[x].val))%3!=z-1)//判断是否冲突
21             return 1;
22     }
23     else
24     {
25         a[ty].root=tx;
26         a[ty].val=((z-1)+(3-a[y].val)+a[x].val)%3;//算关系
27     }
28     return 0;
29 }
30 int main()
31 {
32     scanf("%d%d",&n,&m);
33     for(int i=1;i<=n;i++)
34     {
35         a[i].root=i;
36         a[i].val=0;
37     }
38     while(m--)
39     {
40         int x,y,z;
41         scanf("%d%d%d",&z,&x,&y);
42         if(x>n||y>n)ans++;
43         else if(z==2&&x==y)ans++;
44         else ans+=merge(z,x,y);//合并
45     }
46     cout<<ans;
47 }

其实种类并查集就是权值并查集一个特殊的存在,总而言之都是并查集就对了(滑稽)

转载于:https://www.cnblogs.com/hualian/p/11350931.html

【常用数据结构——并查集(又在乱牵线了)】相关推荐

  1. (转载)一种简单而有趣的数据结构——并查集

    一种简单而有趣的数据结构--并查集 作者:goal00001111(高粱) 一个秘密生物武器落到某地区,导致当地村民丧失部分记忆,只认得自己最熟悉的人,而忘记自己是哪个村子的人了.大家汇集到一个广场, ...

  2. 一种简单而有趣的数据结构——并查集

    一种简单而有趣的数据结构--并查集 作者:goal00001111(高粱) 一个秘密生物武器落到某地区,导致当地村民丧失部分记忆,只认得自己最熟悉的人,而忘记自己是哪个村子的人了.大家汇集到一个广场, ...

  3. 高级数据结构---并查集

    高级数据结构-并查集 原理:参考趣学数据结构 代码: #include<stdio.h> #include<stdlib.h> #define N 100 int father ...

  4. 数据结构---并查集

    并查集,顾名思义,合并 查找 集合: 并查集是一种树型的数据结构,用于处理一些不相交集合(Disjoint Sets)的合并及查询问题.常常在使用中以森林来表示. 对于概念等等的这里不再赘述,直接讲解 ...

  5. 算法与数据结构——并查集

    文章推荐:[算法与数据结构]-- 并查集 例子: 数据结构--最小生成树之克鲁斯卡尔算法(Kruskal) 1.2 并查集思想(重点) 我们可以把每个连通分量看成一个集合,该集合包含了连通分量的所有点 ...

  6. 【总结】C++ 高级数据结构 —— 并查集用法详解

    文章目录 一.并查集的介绍 二.并查集的基本操作 一.并查集的介绍 并查集(union_find sets)是一种维护集合的数据结构,它的名字中"并""查"&q ...

  7. 第三十一篇 玩转数据结构——并查集(Union Find)

    1.. 并查集的应用场景 查看"网络"中节点的连接状态,这里的网络是广义上的网络 数学中的集合类的实现 2.. 并查集所支持的操作 对于一组数据,并查集主要支持两种操作:合并两个数 ...

  8. 并查集与贪心算法的应用之求解无向图的最小生成树

    一,介绍 本文介绍使用Kruskal算法求解无向图的最小生成树.Kruskal是一个贪心算法,并且使用了并查集这种数据结构. 关于并查集的介绍,参考:数据结构--并查集的原理及实现 二,构造一个无向图 ...

  9. LeetCode 2092. 找出知晓秘密的所有专家(并查集)

    文章目录 1. 题目 2. 解题 1. 题目 给你一个整数 n ,表示有 n 个专家从 0 到 n - 1 编号. 另外给你一个下标从 0 开始的二维整数数组 meetings ,其中 meeting ...

最新文章

  1. 介绍一下你对浏览器内核的理解?
  2. 摄像头自动曝光_OPPO新专利曝光:手写笔不仅仅只是手写笔 它还是手机的额头...
  3. asp网络编程:用ASP打开远端MDB文件的方法
  4. IIS HTTP 错误 404.17 - Not Found 解决方法
  5. 释疑の舍入参数文件介绍
  6. html框架集把那根框架隐藏,HTML 框架集 frameset 和内嵌框架 iframe
  7. baidu的服务器数据里面装的都是垃圾!
  8. 行添加DataGridView导出Excel的数据表格
  9. php之获取ip(网站地址)
  10. 12步让你的web1.0变成web2.0
  11. Benchmark与Profiler---性能调优得力助手
  12. Maxwell 介绍
  13. 明白熊猫与冰墩墩的区别,就知道青云KubeSphere与QKCP该如何选择
  14. 记一次高德地图引入 AMap is not defined 血坑
  15. 淘宝电商产品jQuery图片放大镜代码
  16. 2020机修钳工(中级)考试及机修钳工(中级)复审模拟考试
  17. 描述: 一注完整的双色球彩票号码由5个红色号码,2个蓝色号码组成 共七位数注意: 只要刷新一次页面 让球发生一次变化
  18. 老Java程序员谈谈swing要不要学
  19. 车祸相关公开数据集(免费下载)
  20. 探究论文检测系统抄袭的算法原理等技术研究

热门文章

  1. 后缀表达式看完这一篇文章就理解了!
  2. 链游StarSharks(星鲨)全方位介绍
  3. iPhoneX开了个好头,苹果今年将推廉价版iPhoneX柏颖
  4. 无法更新到Win8.1的原因与解决办法
  5. 域名与服务器如何绑定?
  6. 10.3_word2vec-pytorch
  7. 首家共享充电宝倒闭了,王思聪要赌赢了吗?
  8. pyinstaler打包paddle
  9. 哈希表的概念(散列表)
  10. SAP ABAP openSQL数据库操作(四)