수학에서 서로소 집합(Disjoint Sets)이란 공통 원소가 없는 두 집합을 의미한다.
예를 들어 집합 {1, 2}와 집합 {3, 4}는 서로소 관계이다. 반면에 집합 {1, 2}와 {2, 3}은 2라는 원소가 두 집합에 공통적으로 포함되어 있기 때문에 서로소 관계가 아니다.
서로소 집합 정보(합집합 연산)가 주어졌을 때 트리 자료구조를 이용해서 집합을 표현하는 서로소 집합 계산 알고리즘은 다음과 같다.
부모 테이블은 말 그대로 부모에 대한 정보만을 담고 있다. 다시 말해 특정한 노드의 부모에 대해서만 저장하고 있다. 우리가 실제로 루트를 확인하고자 할 때는 재귀적으로 부모를 거슬러 올라가서 최종적인 루트 노드를 찾아야 한다.
노드 1과 노드 4의 루트 노드를 각각 찾는다. 현재 루트 노드는 각각 1과 4이므로 더 큰 번호에 해당하는 루트 노드 4의 부모를 1로 설정한다.
노드 2와 노드 3의 루트 노드를 각각 찾는다. 현재 루트 노드는 각각 2와 3이므로 더 큰 번호에 해당하는 루트 노드 3의 부모를 2로 설정한다.
노드 2와 노드4의 루트 노드를 각각 찾는다. 현재 루트 노드는 각각 2와 1이므로 더 큰 번호에 해당하는 루트 노드 2의 부모를 1로 설정한다.
노드 5와 노드 6의 루트 노드를 각각 찾는다. 현재 루트 노드는 각각 5와 6이므로 더 큰 번호에 해당하는 루트 노드 6의 부모를 5로 설정한다.
private var v: Int = 0 // 노드의 개수
private var e: Int = 0 // 간선의 개수(Union 연산)
private val parent = IntArray(100001) // 부모 테이블
// 특정 원소가 속한 집합 찾기
private fun findParent(x: Int): Int {
// 루트 노드가 아니라면, 루트 노드를 찾을 때까지 재귀적으로 호출
if (x == parent[x]) return x
return findParent(parent[x])
}
private fun unionParent(a: Int, b: Int) {
val xa = findParent(a)
val xb = findParent(b)
if (xa < xb) parent[xb] = xa
else parent[xa] = xb
}
fun main() {
val sc = Scanner(System.`in`)
v = sc.nextInt()
e = sc.nextInt()
// 부모 테이블상에서, 부모를 자기 자신으로 초기화
for (i in 1 .. v) {
parent[i] = i
}
// Union 연산을 각각 수행
for (i in 0 until e) {
val a = sc.nextInt()
val b = sc.nextInt()
unionParent(a, b)
}
// 각 원소가 속한 집합 출력하기
print("각 원소가 속한 집합: ")
for (i in 1 .. v) {
print("${findParent(i)} ")
}
println()
// 부모 테이블 내용 출력하기
print("부모 테이블: ")
for (i in 1 .. v) {
print("${parent[i]} ")
}
println()
}
[기존 방법]
// 특정 원소가 속한 집합 찾기
private fun findParent(x: Int): Int {
// 루트 노드가 아니라면, 루트 노드를 찾을 때까지 재귀적으로 호출
if (x == parent[x]) return x
return findParent(parent[x])
}
[경로 압축]
private fun findParentPathCompression(x: Int): Int {
if (x == parent[x]) return x
return findParent(parent[x]).also { parent[x] = it }
}