原题地址
https://pintia.cn/problem-sets/1268384564738605056/problems/1278908289143574529
解题思路
考查二叉树的应用中的集合运算。
注意事项
1.我这个憨批啊!!合并集合的时候要先更新集合规模然后再赋根结点的值!!这个顺序搞反了导致我找了一个小时的bugQAQ
2.记得用Find的压缩路径算法,优化查找效率!
3.一开始写, 读取数据的时候忘记getchar()
什么的,导致又debug很久的事情我才不会告诉别人呢~
参考代码
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1e4 + 10;
typedef int ElementType;
typedef int SetName; //默认用根节点的下标作为集合名称
typedef ElementType SetType[MAXN]; //假设集合元素下标从0开始
SetType S;
//查找某个元素所在的集合
//默认集合元素初始化为-1
SetName Find0(SetType S, ElementType X) {
for (; S[X] >= 0; X = S[X]);
return X;
}
//Find的路径压缩算法
SetName Find(SetType S, ElementType X) {
//若值为负,说明到达根结点,返回根结点的下标
if (S[X] < 0) return X;
else return S[X] = Find(S, S[X]);
}
//集合的合并
void Union(SetType S, SetName Root1, SetName Root2) {
//根结点对应的值为负数,其绝对值为集合元素个数
if (S[Root2] > S[Root1]) {//集合2比集合1小
S[Root1] += S[Root2]; //更新集合规模
S[Root2] = Root1; //注意这里的顺序!!!
//一定要先更新规模再赋值QAQ
} else {
S[Root2] += S[Root1];
S[Root1] = Root2;
}
}
int main() {
fill(S, S + MAXN, -1);
int n, n1, n2;
scanf("%d", &n);
char c;
getchar(); //球球了不要再忘记把这个换行符给弄掉了
while (~scanf("%c", &c)) {
if (c == 'S') break;
scanf(" %d %d\n", &n1, &n2); //还有你!
if (c == 'C') {
//若两个都不是单独的结点,且根结点相同,则说明在同一个集合里
if (Find(S, n1) == Find(S, n2) && S[n1] != -1 && S[n2] != -1) {
printf("yes\n");
} else printf("no\n");
}
else {
//先用压缩路径的算法找出二者根结点
int root1 = Find(S, n1);
int root2 = Find(S, n2);
//判断不是同一个根结点,则进行合并
if (root1 != root2)
Union(S, root1, root2);
}
}
//对每个结点都进行压缩路径,便于最后的计数
for (int i = 1; i <= n; ++i) {
Find(S, i);
}
int cnt = 0;
for (int i = 1; i <= n; ++i) {
if (S[i] < 0) cnt++;
}
if (cnt == 1) printf("The network is connected.");
else printf("There are %d components.", cnt);
return 0;
}