비교 하고자 하는 value 2개가 집합 상태에 있는지, 아니면 2개를 합해서 하나의 집합으로 만드는 알고리즘

알고리즘에서는 트리는 소속관계를 명시하는 자료구조이다.
그래서
어느 한 친구를 대장으로 하고, 대장값만 가지고 있다면, 서로 같은 집합인지 알 수 있다.


생각해보기
- 그렇다면 집합에 새로운 친구가 추가될 때 마다. 1번 루트 아래로 주렁주렁 매달려서 높이가 높아질 것이다.
-> 부모 찾을 때 재귀로 하자고 했는데, 트리 높이가 길어지므로 비효율적.
(pV[1] = 1 , pV[2] = 1, pV[3] = 2, pV[4] = 3; // 부모값을 가지고 또 재귀를 해서 1번 값이 루트값이네... 그런데 재귀가 많다.)
경로 압축
: 재귀를 할 때마다 그냥 pV[num] = Find(pV[num]) 대입해버리기
(pV[1] = 1 , pV[2] = 1, pV[3] = 1, pV[4] = 1;)
이렇게 해버리면 매번 재귀해서 1번을 찾을 필요 없다.
동시에 그냥 부모 찾을때 반환해야 하므로, 아래의 코드처럼 작성함.
return pV[num] = Find(pv[num]);

Find 함수에서 내가 루트인지를 작성하면서 초기화를 어떻게 할까? 생각할 수 있따.
나의 정점이 루트값이라고 한다면, 그냥 내번호를 반환하면 된다.
따라서 모든 정점은 자신의 번호로 초기화 하자.
- 만약에 부모가 생겨서 변경된다면, Union에서 해줄거다!

정책
- 어떤 친구를 부모노드로 지정할까??
: 값이 작은 값을 부모노드로 지정함.

#include <iostream>
#include <vector>
#include <string>
using namespace std;
vector<int> parentV;
int Find(int a)
{
// 1(root) - 2 - 3 으로 연결된 집합이 있고,
// 맨 처음에 자기 자신이면 아직 union된게 없다거나
// 아니면 내가 root
if (parentV[a] == a)
return a;
return parentV[a] = Find(parentV[a]);
}
void Union(int a, int b)
{
// 누구를 parent로 할 것인가??
// 값이 낮은 걸로 하자.
// 그러데
// 1(root) - 2 - 3 으로 연결된 집합이 있고,
// UNION 으로 3번과 4번을 하려고 한다.
// 이 때는 4번 친구도 1번을 부모로 해야 한다.
// => 그럼 부모값을 알아야 한다.
// 이에 유념하면서 작성하자.
int parentA = Find(a);
int parentB = Find(b);
// 규칙을 내가 정하자.
// 부모가 동일하면 할 필요 없다.
// 작은 값이 부모가 되게 하자.
if (parentA < parentB)
{
parentV[b] = parentA;
}
else if (parentA > parentB)
{
parentV[a] = parentB;
}
}
int main()
{
int n, m;
cin >> n >> m;
parentV.resize(m);
for (int i = 0; i <= n; ++i)
{
parentV[i] = i;
}
// 두개의 값이 서로 집합 관계인지를
// 확인하는 문제이므로, 유니온 파인드 문제다!
for (int i = 0; i < m; ++i)
{
int a, b, c;
cin >> a >> b >> c;
if (a == 0) // 합집합하는 연산.
{
Union(b, c);
}
else
{
// 두 원소가 합인지 아닌지를 확인하는 연산.
int bb = Find(b);
int cc = Find(c);
if (bb == cc)
{
cout << "YES" << endl;
}
else
cout << "NO" << endl;
}
}
return 0;
}