一、什么是并查集?
首先字面意思是把相互联系的元素通过特定查询组成一个集合。规范化解释:并查集,在一些有N个元素的集合应用问题中,我们通常是在开始时让每个元素构成一个单元素的集合,然后按一定顺序将属于同一组的元素所在的集合合并,其间要反复查找一个元素在哪个集合中。
二、并查集应用场景:
1. 图的连通性,可以用来判断哪些节点是连通的。也可以知道一个图一共能被分成几个相互独立的块。
2. 区间类问题
3. 连续子序列问题
4. 经典最小生成树算法:Kruskal 算法
5. 等等… 后期补充
总而言之,凡是涉及到元素的分组管理问题,都可以考虑使用并查集进行维护。
这一类问题近几年来反复出现在信息学的国际国内赛题中。其特点是看似并不复杂,但数据量极大,若用正常的数据结构来描述的话,往往在空间上过大,计算机无法承受;即使在空间上勉强通过,运行的时间复杂度也极高,根本就不可能在比赛规定的运行时间(1~3秒)内计算出试题需要的结果,只能用并查集来描述。
三、并查集详解:
先引入一道经典例题: 洛谷 P1551 亲戚

输入样例:
6 5 3
1 2
1 5
3 4
5 2
1 3
1 4
2 3
5 6
输出样例:
Yes
Yes
No
看到这道题,我的第一想法就是按每个人之间的联系建立邻接表,然后进行dfs遍历,每次dfs以后把结果存起来。当判断两个人是否是亲戚关系时,就在刚才存的dfs的所有集合中找两个人是否在同一集合,如果在则说明是亲戚,否则不是。
实现代码:
#include <bits/stdc++.h>
using namespace std;
int n,m,p;
int x,y;
vector<vector<int>> relationship(10000);
int vis[10000] = {
0};
vector<vector<int>> v;
void dfs(int start, vector<int> &v) {
vis[start] = 1;
for (int i = 0; i < relationship[start].size(); ++i) {
if (vis[relationship[start][i]] == 0) {
v.push_back(relationship[start][i]);
dfs(relationship[start][i], v);
}
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie();cout.tie();
cin>>n>>m>>p;
// 建立邻接表
for (int i = 0; i < m; ++i) {
cin>>x>>y;
relationship[x].push_back(y);
relationship[y].push_back(x);
}
// dfs 深度搜索
for (int i = 1; i <= n; ++i) {
if (vis[i] == 1) continue;
vector<int> a; // 存储每次dfs的结果
a.push_back(i);
dfs(i, a

本文详细介绍了并查集数据结构的基本概念、应用场景、实现原理及其优化方法,并提供了具体实例代码。
595

被折叠的 条评论
为什么被折叠?



