并查集
并查集是一种树型的数据结构,用于处理一些不相交集合(Disjoint Sets)的合并及查询问题。
数组实现集合操作
初始化数组为-1,对于数值,如果为负值表示为祖先节点,负值越小表示此集合节点越多,从而方便并集操作,实现高度压缩,正值为父节点
关键代码:
int myfind(int i) { //实现查找祖先节点并缩短树高,使其子代节点向父节点集中
if (arr[i] < 0) //数组的初始化是祖先为自身,即节点值为下标
return i; //祖先为自身即返回
return arr[i] = myfind(arr[i]); //如果祖先不是自身,递归查找祖先,同时将当前节点和随后的父节点连接到祖先节点上,简化树
}
void join(int a, int b) { //将ab组合加入到集合中
int fa = myfind(a); //拿到a和b的祖先
int fb = myfind(b);
int temp = arr[fa] + arr[fb];
if (fa != fb) { //判断ab是否为同一祖先
if (arr[fa] < arr[fb]) { //如果fa更小,说明此集合节点更多
arr[fb] = fa; //就把fb为祖先的集合连接到fa上
arr[fa] = temp; //更新祖先fa的节点数量
}
else {
arr[fa] = fb;
arr[fb] = temp;
}
}
}
例题
#include <iostream>
using namespace std;
int arr[10001];
int myfind(int i) {
if (arr[i] < 0) {
return i;
}
return arr[i] = myfind(arr[i]);
}
void join(int i, int j) {
int fa = myfind(i);
int fb = myfind(j);
int temp = arr[fa] + arr[fb];
if (fa != fb) {
if (arr[fa] < arr[fb]) {
arr[fb] = fa;
arr[fa] = temp;
}
else {
arr[fa] = fb;
arr[fb] = temp;
}
}
}
int main() {
for (int i = 0; i < 10001; i++) {
arr[i] = -1;
}
int n;
cin >> n;
int sum = 0; //sum记录节点总数
for (int i = 0; i < n; i++) {
int m;
cin >> m;
int num1, num2; //此题只需要每组数据的第一个与其他的数据所代表的集合并集就行,所以先储存第一个数num1,num2为后续数
for (int j = 0; j < m; j++) {
cin >> num2;
if (sum < num2) sum = num2; //更新sum值求节点总数,因为由题意得最大节点表示人群数量
if (j == 0) {
num1 = num2; //拿到第一个数
}
else {
join(num1, num2);
}
}
}
cout << sum << " ";
int sump = 0;
for (int i = 1; i <= sum; i++) { //遍历数组,节点值为负,即为祖先节点
if (arr[i] < 0) {
sump++;
}
}
cout << sump << endl;
int t;
cin >> t;
int t1, t2;
for (int i = 0; i < t; i++) {
cin >> t1 >> t2;
if (myfind(t1) == myfind(t2)) { //判断两个人是否在同一部落,判断是否为同一祖先即可
cout << "Y" << endl;
}
else {
cout << "N" << endl;
}
}
return 0;
}
/*额外测试数据
5
3 10 1 2
3 3 4 5
3 1 7 8
3 9 6 4
2 1 4
2
10 5
3 7
*/