상호 배타 집합 알고리즘
- Disjoint Set 알고리즘, 분리집합 알고리즘, Union-Find 알고리즘 이라고도 불림
- 그래프, 트리에서 많이 사용
- 동일한 집합에 속하는지 여부를 판단하고, 서로 다른 집합을 합치는 연산에 관한 알고리즘
상호 배타 집합의 주요 연산
1. Make-Set
새로운 집합을 생성하는 연산. 자기 자신을 부모로 설정하여 트리 형태로 관리
2. Find-Set
특정 요소가 속한 집합을 찾는 연산.
가장 최상단의 부모 노드를 찾아 반환하는 형태로 구현된다.
이를 통해 두 노드가 서로 같은 부모를 공유하면 같은 집합에 속한 것이고,
다른 부모를 가진다면 서로 다른 집합에 속한 것임을 알 수 있다.
3. Union
두 집합을 하나로 병합하는 연산.
BFS가 필요한 상황?
1. 그래프의 연결 여부 판단: 그래프 노드가 연결되어있는지 판단할 수 있다. 최소신장트리 알고리즘 등에서 사용된다.
2. 사이클 검사 : 무방향 그래프에서 사이클이 있는지 여부를 확인할 때 사용
장점
- 집합 관리에 효율적이다. 시간복잡도가 거의 상수 시간에 가까워진다.
- 크루스칼 알고리즘과 같은 최소 신장 트리 알고리즘에서 그래프의 연결성 관리하는 데 필수적이다.
단점
- 새로운 요소를 추가하는 연산이 없어 고정된 집합에서만 사용 가능
- 트리 구조로 관리하는 형태이기 때문에, 트리 높이를 효율적으로 유지하기 위한 추가적 연산 필요
구현
public class DisjointSetArray {
// Make-Set: 각 원소가 자신을 부모로 갖는 새로운 집합을 생성
public static void makeSet(int[] parent, int[] rank, int n) {
for (int i = 0; i < n; i++) {
parent[i] = i; // 자신이 부모
rank[i] = 0; // 초기 랭크는 0
}
}
// Find-Set: x가 속한 집합의 루트(대표자)를 찾음, 경로 압축 적용
public static int findSet(int[] parent, int x) {
if (parent[x] != x) {
parent[x] = findSet(parent, parent[x]); // 경로 압축
}
return parent[x];
}
// Union: 두 집합을 병합, 랭크를 기준으로 병합
public static void union(int[] parent, int[] rank, int x, int y) {
int rootX = findSet(parent, x);
int rootY = findSet(parent, y);
if (rootX != rootY) {
// 랭크를 기준으로 작은 트리를 큰 트리 밑에 붙임
if (rank[rootX] > rank[rootY]) {
parent[rootY] = rootX; // rootY를 rootX에 병합
} else if (rank[rootX] < rank[rootY]) {
parent[rootX] = rootY; // rootX를 rootY에 병합
} else {
parent[rootY] = rootX; // 둘의 랭크가 같을 때
rank[rootX]++; // rootX의 랭크 증가
}
}
}
public static void main(String[] args) {
int n = 5; // 5개의 원소
int[] parent = new int[n]; // 부모 배열
int[] rank = new int[n]; // 랭크 배열
// Make-Set: 5개의 원소 각각을 독립적인 집합으로 생성
makeSet(parent, rank, n);
// Union 연산: 1과 2를 병합, 3과 4를 병합, 2와 3을 병합
union(parent, rank, 1, 2);
union(parent, rank, 3, 4);
union(parent, rank, 2, 3);
// Find-Set 연산: 1과 4가 같은 집합에 속하는지 확인
if (findSet(parent, 1) == findSet(parent, 4)) {
System.out.println("1과 4는 같은 집합에 속합니다.");
} else {
System.out.println("1과 4는 다른 집합에 속합니다.");
}
// Find-Set 연산: 5는 어느 집합에 속하는지 확인
System.out.println("5의 대표: " + findSet(parent, 4));
}
}