定义:
并查集是一种树型的数据结构,用于处理一些不相交集合(Disjoint Sets)的合并及查询问题。常常在使用中以森林来表示。主要应用在一些有N个元素的集合应用问题中,如开始时让每个元素构成一个单元素的集合,然后按一定顺序将属于同一组的元素所在的集合合并,其间要反复查找一个元素在哪个集合中。
并查集的基本概念:
- 集合:在并查集中,一个集合是由一组元素组成的,这些元素之间具有某种关系,使得它们可以被视为一个整体。每个集合都有一个唯一的代表元素,通常称为该集合的根节点或代表。
- 树形结构:并查集使用树形结构来表示集合之间的关系。在树中,每个节点代表集合中的一个元素,而指针(或链接)则表示元素之间的父子关系。树的根节点代表整个集合的代表元素。
并查集的基本操作:
- 初始化(Make_Set):在开始时,每个元素都被视为一个独立的集合。因此,初始化操作就是将每个元素作为一个单独的树,即每个元素的父节点都是它自己。
- 合并(Union_Set):合并操作是将两个集合合并成一个集合。为了实现这一操作,我们需要找到两个集合的代表元素(即根节点),然后将其中一个根节点的父节点设置为另一个根节点,表示将这两个集合合并成一个集合。这样,合并后的集合的代表元素就是新设置的根节点。
- 查找(Find):查找操作是查询一个元素属于哪个集合。为了找到元素所在的集合,我们需要从该元素开始,沿着父节点指针不断向上查找,直到找到根节点为止。这个根节点就是该元素所在的集合的代表元素。
路径压缩技术:
为了提高查找效率,我们可以使用路径压缩技术。在每次查找操作中,我们将查找路径上的每个节点的父节点直接设置为根节点。这样,下次查找时就可以直接从节点跳到根节点,无需再遍历整个路径。这种技术可以大大降低树的高度,从而提高查找效率。
并查集的应用:
并查集在许多实际问题中有广泛的应用,例如:
- 连通性问题:在图形理论中,可以使用并查集来判断图中的节点是否属于同一个连通分量。
- 最小生成树问题:在构建最小生成树时,可以使用并查集来合并已连接的节点所属的集合,从而确保生成的树是连通的且边的权值和最小。
- 社交网络分析:在社交网络中,可以使用并查集来分析用户之间的关系,例如判断两个用户是否属于同一个社交圈子。
总之,并查集是一种高效处理不相交集合问题的数据结构,通过树形结构和路径压缩技术,可以实现快速的集合合并和查询操作。总的来说,并查集是一种高效处理不相交集合问题的数据结构,其时间复杂度接近线性,空间复杂度为O(n)。在实际应用中,我们可以使用并查集来解决一些需要快速合并和查找集合的问题,如连通性问题、最小生成树问题等。
题目实现:(并查集)
输入格式
第一行包含两个整数 N,M ,表示共有 N 个元素和 M 个操作。
接下来 M 行,每行包含三个整数 Zi,Xi,Yi 。
当Zi=1 时,将 Xi 与 、Yi 所在的集合合并。
当Zi=2 时,输出Xi 与Yi 是否在同一集合内,是的输出
Y
;否则输出N
。输出格式
对于每一个 Zi=2 的操作,都有一行输出,每行包含一个大写字母,为
Y
或者N
。输入输出样例
输入 #1复制
4 7 2 1 2 1 1 2 2 1 2 1 3 4 2 1 4 1 2 3 2 1 4
输出 #1复制
N Y N Y
说明/提示
对于 30% 的数据,N≤10,M≤20。
对于 70% 的数据,N≤100,M≤103。
对于 100% 的数据,1≤N≤104,1≤M≤2×105,1≤Xi,Yi≤N,Zi∈{1,2}。
代码实现:
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#define MAXSIZE 10100 // 定义并查集的最大大小为10100
// 定义两个全局数组,father数组存储每个节点的父节点,size数组存储每个集合的大小
int father[10100], size[10100];
// 初始化函数,为并查集的所有节点设置初始的父节点为自身
void make(int size) {
for (int i = 1; i <= size; i++)
father[i] = i; // 以自己为代表
}
// 查找函数,使用路径压缩技术,查找节点i的根节点
int find(int i) {
if (i != father[i]) // 如果当前节点不是根节点
father[i] = find(father[i]); // 则递归地查找它的父节点的根节点,并进行路径压缩
return father[i]; // 返回根节点
}
// 判断两个节点是否属于同一个集合
bool Set(int x, int y)
{
return find(x) == find(y); // 如果它们的根节点相同,则它们属于同一个集合
}
// 合并两个集合,即将集合x的根节点设置为集合y的根节点
void un(int x, int y) {
father[find(x)] = find(y);
}
int main()
{
int n, m, z, x, y;
scanf("%d%d", &n, &m); // 读取节点的数量和操作的数量
make(n); // 初始化并查集
while (m--) { // 对每个操作进行处理
scanf("%d%d%d", &z, &x, &y); // 读取操作类型和两个节点的编号
if (z == 1) { // 如果操作类型为1,表示要合并两个集合
if (!Set(x, y)) // 如果x和y不在同一个集合中
un(x, y); // 则合并这两个集合
}
else { // 如果操作类型为2,表示要查询两个节点是否属于同一个集合
if (Set(x, y)) // 如果x和y在同一个集合中
printf("Y\n"); // 则输出"Y"
else
printf("N\n"); // 否则输出"N"
}
}
return 0;
}