并查集

并查集

丶灬走出姿态 提交于 2020-02-17 05:36:44
并查集 并查集,在一些有N个元素的集合应用问题中,我们通常是在开始时让每个元素构成一个单元素的集合,然后按一定顺序将属于同一组的元素所在的集合合并,其间要反复查找一个元素在哪个集合中。这一类问题近几年来反复出现在信息学的国际国内赛题中,其特点是看似并不复杂,但数据量极大,若用正常的数据结构来描述的话,往往在空间上过大,计算机无法承受;即使在空间上勉强通过,运行的时间复杂度也极高,根本就不可能在比赛规定的运行时间(1~3秒)内计算出试题需要的结果,只能用并查集来描述。 (百度百科) 例题 小郑作为新生,刚刚来到美丽的比斯猪,学校规定每见到一个陌生人都要上去握手并成为好朋友,但为了避免把全校同学手都握了,学校还规定只要遇见一个陌生人,如果这个陌生人是他朋友认识的,那就是自己认识的,就不和她握手(朋友的朋友就是我的朋友,只要是能通过朋友关系串联起开,不管多远,都不握手) 这样问题就来了,因为学校里的同学实在太多了,如果要一个个问朋友认不认识这个人,再问朋友的朋友认不认识那个人。这样问下来一整天也不一定能够知道这个人自己到底认不认识。 int person[100];int find(int root)//查找认识的最远的人{int son = root; while(root != person[root])//先找出认识的最远的人(根节点) root = person[root];

pat甲级1107 并查集

喜你入骨 提交于 2020-02-17 02:18:56
并查集在findFather()函数中进行压缩路径,陷阱是这里只压缩该结点以上到根的路径,其以下的路径不压缩,这里不搞清楚会有三个测试点过不去 # include <cstdio> # include <vector> # include <algorithm> using namespace std ; int N ; vector < int > hobby [ 1001 ] ; int father [ 1001 ] ; int cluster [ 1001 ] = { } ; int findFather ( int a ) ; void unionElem ( int a , int b ) ; bool cmp ( int a , int b ) ; int main ( ) { scanf ( "%d" , & N ) ; for ( int i = 1 ; i <= N ; i ++ ) father [ i ] = i ; //并查集初始化 for ( int i = 1 ; i <= N ; i ++ ) { int ct , h ; scanf ( "%d:" , & ct ) ; while ( ct -- ) { scanf ( "%d" , & h ) ; hobby [ h ] . push_back ( i ) ; } } for ( int i =

并查集

半世苍凉 提交于 2020-02-16 23:32:08
文章目录 并查集 并查集问题中集合如何存储 实现 并查集 集合的运算 :交、并、补、差和判断一个元素是否属于某一集合。 并查集 :集合的合并、判断一个元素是否属于某一集合的操作。 并查集问题中集合如何存储 可以用树结构表示集合,每棵树代表一个集合,树的每个节点一个集合的元素。例: 怎么更加方便的表示一棵树?答案是用数组。数组元素类型如下: Typedef struct SetNode { ElementType Data ; //存储数据 int Parent ; //存储父节点在数组中的下标;如果本身就是父节点,就用负数表示,负数绝对值的大小可以用来确定这棵树的高度 } SetType ; 例: 表示的集合为: 实现 查找一个元素在哪个集合 int Find ( SetType S [ ] , ElementType X ) { /* 在数组S中查找值为X的元素所属的集合 */ /* MaxSize是全局变量,为数组S的最大长度 */ int i ; for ( i = 0 ; i < MaxSize && S [ i ] . Data != X ; i ++ ) ; if ( i >= MaxSize ) return - 1 ; /* 未找到X,返回-1 */ for ( ; S [ i ] . Parent > 0 ; i = S [ i ] . Parent ) ;

洛谷P1536 村村通 题解 并查集

筅森魡賤 提交于 2020-02-16 22:40:47
题目链接: https://www.luogu.com.cn/problem/P1536 题目大意:告诉你一些点属于同一个集合,求最少连多少条边能够合成一个集合。 解题思路: 最少连边数 = 集合数 - 1。 实现代码如下: #include<bits/stdc++.h> using namespace std; const int maxn = 10010; int n, m, f[maxn], cnt; bool vis[maxn]; int func_find(int x) { return x == f[x] ? x : f[x] = func_find(f[x]); } int func_union(int x, int y) { int a = func_find(x), b = func_find(y); f[a] = f[b] = f[x] = f[y] = min(a, b); } void init() { for (int i = 1; i <= n; i ++) f[i] = i; for (int i = 1; i <= n; i ++) vis[i] = false; cnt = 0; } int main() { while (cin >> n) { if (n == 0) break; cin >> m; init(); while (m --) {

并查集

ⅰ亾dé卋堺 提交于 2020-02-16 19:14:32
//查并集 /* 一共有n个数,编号是1~n,最开始每个数各自在一个集合中。 现在要进行m个操作,操作共有两种: “M a b”,将编号为a和b的两个数所在的集合合并,如果两个数已经在同一个集合中,则忽略这个操作; “Q a b”,询问编号为a和b的两个数是否在同一个集合中; 4 5 M 1 2 M 3 4 Q 1 2 Q 1 3 Q 3 4 Yes No Yes */ # include <iostream> # include <cstring> using namespace std ; const int N = 100010 ; int p [ N ] ; //存储每个店的祖宗节点 //返回x所在集合的祖宗节点下标,从1开始,并内部更新p[x]为a节点的祖先节点 int find ( int a ) { if ( p [ a ] != a ) p [ a ] = find ( p [ a ] ) ; return p [ a ] ; } int main ( ) { int n , m ; cin >> n >> m ; for ( int i = 1 ; i <= n ; i ++ ) p [ i ] = i ; int a , b ; char op [ 2 ] ; while ( m -- ) { cin >> op >> a >> b ; if ( op [ 0

入门——并查集

最后都变了- 提交于 2020-02-16 14:10:28
网上有很多大佬,写的十分清楚,看他们的博客就能入门,我就不卖弄了。这里就先引用一个: https://blog.csdn.net/niushuai666/article/details/6662911 以下是补充说明: (个人领悟仅供参考) 并查集( Union-Find )是一种数据结构的处理方法,主要是处理点与点之间的联系。如果有很多数据之间有简单的关系,你可以把这些数据抽象成一个个点,那么你可以对他们的关系用作并查集。 比如说,有很多人互相认识,他们能通过朋友来结交朋友的朋友,那么给你n个人,告诉你这n个人谁认识谁,然后问你a和b能否成为朋友。再比如几组数,告诉你这两个数有关系,最后问有多少不同种关系,然后还要分别输出和。这些时候你就能使用并查集。 知道什么时候用并查集了,然后是为什么要用并查集。简单的说,就是为了节省查询的时间。像刚才说的找朋友,输入的数据你要存起来,最暴力的做法是有关系就连一起,组成n条关系链表,然后每条链上有m个人,这样等到最后询问的时候,遍历所有的链表,还要根据链表里的数据跳到另一个链表去检索……说都说不清楚,实现起来就更加麻烦了,再顺着这个错误的想法实现下去说不定就要用上字典树了。如果是刚才说的要输出有关系数的和,那暴力的做法也就可以放弃了,根本无从下手——你没有办法处理n条链上相同的数,就算用bool vis标记的话……好了彻底忘掉暴力做法吧

【笔记】并查集

北城余情 提交于 2020-02-16 03:56:35
不管后面写了啥,一定要把最重要的写在前面:(总记不住) 使用并查集前 一定要初始化、一定要初始化、一定要初始化 用 findFather(i) 找根结点,不是 father[i] 数组 并查集的基本操作 使用数组表示并查集: int father [ N ] ; father[i] 表示元素 i 的父亲结点 初始化 初始情况下,每个元素都是单独的集合,因此其父结点是自己,这在并查集中被称为 Reflexive for ( int i = 1 ; i <= N ; i ++ ) { father [ i ] = i ; } 查找与路径压缩 一个集合中只有一个根结点,因此反复地查找父结点,直到 father[x] == x ,则 x 为该集合的根节点。 递推与递归方式查找根(无路径压缩) // 递推方式 int findFather ( int x ) { while ( x != father [ x ] ) { x = father [ x ] ; } return x ; } // 递归方式 int findFather ( int x ) { if ( x == father [ x ] ) return x ; return findFather ( father [ x ] ) ; } 简单的查找方式非常简单,但是对每个结点而言,要查找根总需要反复地向上找,比较费时

JAVA程序设计:账户合并(LeetCode:721)

浪尽此生 提交于 2020-02-15 23:51:48
给定一个列表 accounts,每个元素 accounts[i] 是一个字符串列表,其中第一个元素 accounts[i][0] 是 名称 (name),其余元素是 emails 表示该帐户的邮箱地址。 现在,我们想合并这些帐户。如果两个帐户都有一些共同的邮件地址,则两个帐户必定属于同一个人。请注意,即使两个帐户具有相同的名称,它们也可能属于不同的人,因为人们可能具有相同的名称。一个人最初可以拥有任意数量的帐户,但其所有帐户都具有相同的名称。 合并帐户后,按以下格式返回帐户:每个帐户的第一个元素是名称,其余元素是按顺序排列的邮箱地址。accounts 本身可以以任意顺序返回。 例子 1: Input: accounts = [["John", "johnsmith@mail.com", "john00@mail.com"], ["John", "johnnybravo@mail.com"], ["John", "johnsmith@mail.com", "john_newyork@mail.com"], ["Mary", "mary@mail.com"]] Output: [["John", 'john00@mail.com', 'john_newyork@mail.com', 'johnsmith@mail.com'], ["John", "johnnybravo@mail

图的连通性判断(并查集)

☆樱花仙子☆ 提交于 2020-02-14 13:47:23
判断图的连通性判断方法比较多,最常见的就是并查集、DFS、BFS 这几种,网上的代码也很多,这里主要讲一讲并查集。 利用并查集判断连通性思路为:对于图中的每个节点,设定它的根节点为它本身。对图中的每一条边的两个端点进行合并操作,得到的结果就是相互连通的节点的根节点指向同一个节点。所以只要查询一下结果中节点的根节点等于其本身的节点的数目就可以知道这个图被分成连通的几个部分,如果连通,则为 1。 并查集由一个记录节点的根节点的数组(或者类似容器,字典)和两个函数(find, join)构成。容器(数组)记录了每个点的根节点是哪个,函数 find 用于查找某个节点的根节点,join 函数是合并两个具有相连关系的节点。 pre 字典中保存着每个节点的根节点,find 开始之前要初始化。find 函数经过一层层的寻找,最终找到 x 的根节点,返回。join 函数对边的两个端点所属的集合做合并,如果两个节点的根节点不同,则通过指定其中一个根节点作为另一个根节点的父节点,达到合并所属集合的效果。 最终只需要判断 pre 中本身为本身的父节点的顶点数目即可判断连通性。路径压缩和父节点选择是一种平衡整个关系树的层次的方法,自己体会。 具体代码如下: pre = { } def find ( x ) : r = x while pre [ r ] != r : r = pre [ r ] i = x

POJ 1182 食物链 带权并查集

亡梦爱人 提交于 2020-02-14 06:09:10
动物王国中有三类动物A,B,C,这三类动物的食物链构成了有趣的环形。 A吃B, B吃C,C吃A。 现有N个动物,以1-N编号。 每个动物都是A,B,C中的一种,但是我们并不知道它到底是哪一种。 有人用两种说法对这N个动物所构成的食物链关系进行描述: 第一种说法是”1 X Y”,表示X和Y是同类。 第二种说法是”2 X Y”,表示X吃Y。 此人对N个动物,用上述两种说法,一句接一句地说出K句话,这K句话有的是真的,有的是假的。 当一句话满足下列三条之一时,这句话就是假话,否则就是真话。 1) 当前的话与前面的某些真的话冲突,就是假话; 2) 当前的话中X或Y比N大,就是假话; 3) 当前的话表示X吃X,就是假话。 你的任务是根据给定的N和K句话,输出假话的总数。 输入格式 第一行是两个整数N和K,以一个空格分隔。 以下K行每行是三个正整数 D,X,Y,两数之间用一个空格隔开,其中D表示说法的种类。 若D=1,则表示X和Y是同类。 若D=2,则表示X吃Y。 输出格式 只有一个整数,表示假话的数目。 数据范围 1≤N≤50000, 0≤K≤100000 输入样例: 100 7 1 101 1 2 1 2 2 2 3 2 3 3 1 1 3 2 3 1 1 5 5 输出样例: 3 大体题意就是三种动物组成一个食物链,a吃b,b吃c,c吃a, c为第1代,b为第2代,a为第0代