文章目录
概论
定义:
并查集是一种树型的数据结构,用于处理一些不相交集合的合并及查询问题(即所谓的并、查)。比如说,我们可以用并查集来判断一个森林中有几棵树、某个节点是否属于某棵树等。
主要构成:
并查集主要由一个整型数组pre[ ]和两个函数find( )、join( )构成。
数组 pre[ ] 记录了每个点的前驱节点是谁,函数 find(x) 用于查找指定节点 x 属于哪个集合,函数 join(x,y) 用于合并两个节点 x 和 y 。
作用:
并查集的主要作用是求连通分支数(如果一个图中所有点都存在可达关系(直接或间接相连),则此图的连通分支数为1;如果此图有两大子图各自全部可达,则此图的连通分支数为2……)
代码详解
const int N = 1005 //指定并查集所能包含元素的个数(由题意决定)
int pre[N]; //存储每个结点的前驱结点
int rank[N]; //树的高度
void init(int n) //初始化函数,对录入的 n个结点进行初始化
{
for (int i = 0; i < n; i++) {
pre[i] = i; //每个结点的上级都是自己
rank[i] = 1; //每个结点构成的树的高度为 1
}
}
int find(int x) //查找结点 x的根结点
{
if (pre[x] == x) return x; //递归出口:x的上级为 x本身,则 x为根结点
return find(pre[x]); //递归查找
}
int find(int x) //改进查找算法:完成路径压缩,将 x的上级直接变为根结点,那么树的高度就会大大降低
{
if (pre[x] == x) return x; //递归出口:x的上级为 x本身,即 x为根结点
return pre[x] = find(pre[x]); //此代码相当于先找到根结点 rootx,然后 pre[x]=rootx
}
bool isSame(int x, int y) //判断两个结点是否连通
{
return find(x) == find(y); //判断两个结点的根结点(即代表元)是否相同
}
bool join(int x, int y)
{
x = find(x); //寻找 x的代表元
y = find(y); //寻找 y的代表元
if (x == y) return false; //如果 x和 y的代表元一致,说明他们共属同一集合,则不需要合并,返回 false,表示合并失败;否则,执行下面的逻辑
if (rank[x] > rank[y]) pre[y] = x; //如果 x的高度大于 y,则令 y的上级为 x
else //否则
{
if (rank[x] == rank[y]) rank[y]++; //如果 x的高度和 y的高度相同,则令 y的高度加1
pre[x] = y; //让 x的上级为 y
}
return true; //返回 true,表示合并成功
}
一道例题-小h去旅游
题目描述
小h想去p地旅游,p地有很多个旅游点,旅游点间有些有道路相连,有些没有。小h有若干询问,每个询问有两个数字,表示两个旅游点之间是否有道路相连,a->b->c也意味着a和c相连
输入
第一行三个数字n,m和q表示有n个旅游点(1到n),m条道路,q个查询
后m行每行两个数字u,v表示u,v两个旅游点有道路相连
后q行每行两个数字a,b查询a旅游点和b旅游点间是否有道路相连
(n<=10000,m<=10000,q<=10000)
输出
每个查询输出是否相连,是输出YES反之输出NO
样例输入 Copy
3 2 3
1 2
2 3
3 2
2 1
1 3
样例输出 Copy
YES
YES
YES
#include <iostream>
using namespace std;
int n, m, q;
int pre[10005];
int Find(int x)
{
if (pre[x] == x)
return x;
return pre[x] = Find(pre[x]);
}
int main()
{
scanf("%d %d %d", &n, &m, &q);
for (int i = 0; i < n; i++)
pre[i] = i;
while (m--)
{
int u, v;
scanf("%d %d", &u, &v);
int fx = Find(u), fy = Find(v);
if (fx != fy)
pre[fx] = fy;
}
while (q--)
{
int a, b;
scanf("%d %d", &a, &b);
if (Find(a) == Find(b))
cout << "YES" << endl;
else
cout << "NO" << endl;
}return 0;
}
总结
1、用集合中的某个元素来代表这个集合,则该元素称为此集合的代表元;
2 、一个集合内的所有元素组织成以代表元为根的树形结构;
3 、对于每一个元素 x,pre[x] 存放 x 在树形结构中的父亲节点(如果 x 是根节点,则令pre[x] = x);
4 、对于查找操作,假设需要确定 x 所在的的集合,也就是确定集合的代表元。可以沿着pre[x]不断在树形结构中向上移动,直到到达根节点。
因此,基于这样的特性,并查集的主要用途有以下两点:
1、维护无向图的连通性(判断两个点是否在同一连通块内,或增加一条边后是否会产生环);
2、用在求解最小生成树的Kruskal算法里。
一般来说,一个并查集对应三个操作:
1、初始化( Init()函数 )
2、查找函数( Find()函数 )
3、合并集合函数( Join()函数 )