CS/자료구조

[자료구조] #20 Disjoint Set

태연한 컴공생 2024. 6. 11. 00:15

• Disjoint set (서로소 집합)

 

두 집합의 교집합이 empty하면 disjoint하다고 한다.

입력을 여러 개의 상호 배타적인 그룹으로 나누어 관리할 때 사용한다.

 

• 주요 연산

 

make-set(u) : 원소 u가 주어졌을때 u만을 담는 새로운 집합을 생성한다.

find-set(u) : 입력 원소 u를 담고 있는 집합을 반환한다.

union(u, v) : u를 가지고 있는 집합과 v를 가지고 있는 집합을 합친다.

 

Disjoint set에서는 교집합 연산을 고려할 필요가 없다.

 

• Disjoint Set의 표현

 

Disjoint set은 non-binary tree로 표현한다. 

부모 노드를 가리키는 parent pointer tree를 사용한다.

(각 자식 노드는 부모를 가리키고, 루트 노드는 자기 자신을 가리킨다.)

 

각 트리는 집합 하나를 의미한다.

 

Basic Version의 주요 연산

 

make-set(u)

u를 부모로서 자기 자신을 가리키게 한다.

def make-set(u):
    p[u] ← u

 

find-set(u)

입력 원소 u를 담고 있는 집합의 루트를 반환한다.

노드 u부터 재귀적으로 루트로 올라간다.

def find-set(u):
    if u is p[u]:
        return u
    else:
        return find-set(p[u])

 

union(u, v)

u를 가지는 집합과 v를 가지는 집합을 합친다.

한 집합의 루트가 다른 집합의 루트를 가리키게 한다.

def union(u, v)
    p[find-set(v)] ← find-set(u)

 

Basic Version 분석

 

공간 복잡도

n개의 원소에 대해서 1차원 배열 p의 공간 : O(n) space

 

시간 복잡도

make-set(u) : O(1) time

find-set(u) : O(hu) time

*hu = u를 가지고 있는 집합 (트리)의 높이 

union(u, v) : hv + hu + c time

 

최악의 경우 find-set(u)은 O(n)의 시간이 걸린다.

→ degenerate 형태가 될때

 

위와 같은 최악의 경우를 피하고 더 빠르게 만들 수 있을까?

 

Disjoint는 각 트리의 높이를 줄이면 빠르게 할 수 있다.

(각 연산의 시간이 트리의 높이에 비례하기 때문)

 

1. Union by rank : 더 작은 트리를 큰 트리에 합치기

2. Path compression : find-set을 할 때 루트로 올라가면서 트리를 평평하게 만들기

 

Union By Rank

 

아래와 같이 오른쪽 트리가 왼쪽 트리에 합쳐지면, 트리의 높이는 증가한다.

 

만약 높이가 더 작은 트리를 큰 트리에 합치면 높이 변화가 없다.

 

높이를 빠르게 파악하기 위해서 각 노드별로 rank라는 변수를 관리한다.

def union(u, v):
    ur ← find-set(u)  // ur = u를 가지는 집합의 루트 노드
    vr ← find-set(v)  // v가 속한 집합의 루트 노드를 찾음
    
    if rank[ur] > rank[vr]:  // ur의 트리가 vr의 트리보다 더 큰 경우
        p[vr] ← ur  // vr 트리가 ur의 트리로 합쳐짐
    else:
        p[ur] ← vr  // vr의 트리가 ur의 트리로 합쳐짐
    if rank[ur] == rank[vr]: // 높이가 같다면
        rank[vr] ← rank[vr] + 1 // vr의 트리의 높이를 1증가

 

Path compression

 

find-set을 하면 루트로 거슬러 올라가는데, 올라가면서 마주치는 모든 노드들을 루트로 바로 연결시켜준다.

def find-set(u): 
    if p[u] != u:
       p[u] ← find-set(p[u])
    return p[u]

 

• Union By Rank 분석

 

Claim : union-by-rank를 하면, rank k를 가지는 루트에 의해 표현되는 한 집합의 원소 개수는 최소 2^k개 이상이다.

 

Base case : 만약 rank = 0이라면, 2⁰ = 1개의 원소가 있다.

Inductive step : 랭크 r인 트리가 최소 2^r개의 원소를 가진다고 가정하면, 랭크 r+1인 트리는 최소 2^r+1개의 원소를 가진다.

 

이를 통해 트리의 높이 ≤ rank k ≤ O(log n)이라는 사실을 도출해낼 수 있다.

 

Path Compression 분석

 

make-set(u) : O(1) time

find-set(u) : O (log n) time

union : O (log n) time

 

make-set, find-set, union으로 구성된 m개의 연산 명령이 주어지고, 그 중 n번의 make-set 연산이 수행된다고 가정하면, 위 시퀀스의 전체 시간 복잡도는 O(mlog n)이다.

 

log n은 매우 큰 n에 대해서도 작기 때문에 이를 상수로 취급할 수 있다.

따라서 전체 연산 시퀀스는 O(m) 시간에 수행될 수 있다.

 

평군적으로 각 연산 명령은 O(1) 시간이 걸린다.

즉, path compression이 적용된 disjoint set을 할수록 트리의 높이는 짧아진다.

 

우리는 이러한 분석을 통해 Union-by-rank와 path-compression이 적용된 disjoint-set은 매우 빠른 연산을 지원한다는 사실을 알 수 있다!

'CS > 자료구조' 카테고리의 다른 글

[자료구조] #21 Graph(1)  (0) 2024.06.12
[자료구조] #19 Heap(2)  (0) 2024.06.08
[자료구조] #18 Heap(1)  (0) 2024.06.06
[자료구조] #17 Binary Search Tree(2)  (0) 2024.06.02
[자료구조] #16 Binary Search Tree(1)  (0) 2024.06.02