Upload
jaeho-seok
View
887
Download
6
Embed Size (px)
Citation preview
상호배타적 집합의 처리
기초 알고리즘 스터디
데브루키 꿜라 석재호
상호배타적 집합
분리 집합 (Disjoint Set) 과 같은 표현
의미 : 교집합을 갖지 않는 집합들 .
집합이 단 하나만 존재할 때 공통된 원소의
유무를 따질 수 없으므로
상호배타적이라는 용어는 두 개 이상의 집합에
대해서만 정의가 된다 .
집합의 처리
교집합에 대한 연산은 없다
집합 처리 연산 Make-Set(x) : 원소 ( 데이터 ) x 를 가진 집합을 생성
Find-Set(x) : 원소 x 를 가지는 집합을 알아낸다
Union(x,y) : 원소 x 를 가진 집합과 원소 y 를 가진
집합을 합친다 .
구현 방식 (1/2)
트 리
데이터와 부모에 대한 포인터를 가진 노드
1
2 3 4
5 6
구현 방식 (2/2)
리스트
데이터 , 집합의 루트에 대한 포인터 ,
다음 노드에 대한 포인터를 가진 노드
1 2 3 4
트리를 이용한 구현
* 원소 구조
typedef struct _TREEDISJSET
{
struct _TREEDISJSET* parent;
int data; // 설명의 편의를 위해 int 로 ..
_TREEDISJSET(int _data)
{ data = _data; parent = NULL }
} TreeDS;
트리를 이용한 구현 – 집합 생성
MakeSet
TreeDS* MakeSet(int _data)
{
TreeDS* node = new TreeDS(_data);
return node;
}
트리를 이용한 구현 – 집합 생성
1
3
2
트리를 이용한 구현 – 집합 찾기
FindSet
TreeDS* FindSet(TreeDS* node)
{
while(node->parent != NULL)
node = node->parent;
return node;
}
트리를 이용한 구현 – 집합 찾기
1
2 3 4
5 6
트리를 이용한 구현 – 합집합 연산
UnionSet
void UnionSet(TreeDS* set1, TreeDS* set2)
{
FindSet(set2)->parent = set1;
}
트리를 이용한 구현 – 합집합 연산
1
2 3
4
5 6
set1
set2
1
2 3
4
5 6
리스트를 이용한 구현
원소 구조
typedef struct _LISTDISJSET
{
struct _LISTDISJSET* root;
struct _LISTDISJSET* next;
int data;
_LISTDISJSET(int _data)
{ root = this; next = NULL; data = _data; }
} ListDS;
리스트를 이용한 구현 – 집합 생성
ListDS* MakeSet(int _data)
{
ListDS* node = new ListDS(_data);
return node;
}
리스트를 이용한 구현 – 집합 생성
1
2
3
리스트를 이용한 구현 – 집합 찾기
ListDS* FindSet(ListDS* node)
{
return node->root;
}
리스트를 이용한 구현 – 집합 찾기
1 2 3 4
리스트를 이용한 구현 – 합집합 연산
void UnionSet(ListDS* set1, ListDS* set2)
{
ListDS* tail = set1;
while(tail->next != NULL) tail = tail->next;
tail->next = FindSet(set2);
ListDS* root = FindSet(set1);
while(tail->next != NULL)
{
tail = tail->next;
tail->root = root;
}
}
리스트를 이용한 구현 – 합집합 연산
1 2 3 4
5 6 7
set1
set2
1 2 3 4
5 6 7
최적화 및 응용
작은 것을 큰 것에 붙인다
무게 ( 총 개수 ) 고려한 Union ( 리스트 ) 리스트의 Union 시 붙는 쪽의 집합은
그 원소 개수만큼 root 를 갱신한다
리스트 길이에 대한 정보를 저장한다면
Union 시 긴 쪽에 짧은 쪽을 합침으로써
root 갱신 비용을 줄일 수 있다
작은 것을 큰 것에 붙인다
랭크를 이용한 Union ( 트리 ) 트리의 각 노드에 자신의 서브트리의
높이 값을 저장 (int rank)
Union 시에 두 개의 집합 중 루트의
랭크 값이 높은 쪽 루트에 낮은 쪽을 합친다
-> 구현 간결성이나 효율에 있어서 이점
작은 것을 큰 것에 붙인다
뭐가 되어도 서로 랭크만 다르면
랭크 변화는 어디에도 없다
작은 것을 큰 것에 붙인다
유일하게 랭크가 변화하는 경우 양쪽 루트의 랭크가 같을 경우
작은 것을 큰 것에 붙인다
구현상의 변화 MakeSet : rank 를 0 으로 초기화 O(1)
FindSet : 변화 없음 . O(depth)
UnionSet : 두 집합 루트 랭크 비교 >>
랭크 동일한 경우만 랭크 갱신
랭크는 갈수록 증가하고 , 비용 역시 증가
트리 깊이 줄이기
경로 압축 FindSet 실행 시 , 타고 올라가며 만나는 모든
노드의 parent 를 루트 노드로 갱신
-> 루트 노드에 대한 포인터를 얻을 때까진
갱신이 유예된다
재귀호출이나 스택 푸싱으로 구현
점근적으로는 FindSet 의 복잡도 차이가 없다
트리 깊이 줄이기
최적화 성능의 수학적 증명
랭크 알고리즘을 이용한 트리가n 개 원소를 가질 때의 최대 깊이 : lo g
2n + 1
랭크 알고리즘 특성상 노드 당 1 개의 자식으로 트리를 이어나갈 수 없고 , 자식이 2 개를 넘어가
면 최대 깊이의 경우가 되지 않으므로 밑수는 2 가 됨
깊이에 대한 증명은 다음장에
최적화 성능의 수학적 증명
N-1 회의 Union 연산이 이루어졌다고 할 때
N 번째의 Union 연산을 보면 .. 트리 T1, T2 의 노드 수 = m 개와 N-m 개
1≤m≤N/2 라는 가정이 가능
T2 의 노드 수가 m 개라고 할 때 양 트리의 높이 :
T1 의 높이는 T2 와 동일하거나 T2 보다 1 크다
최적화 성능의 수학적 증명
T1 의 높이 :
T2 의 높이 :
1nlog1m)(nlog 22 +≤+−
1nlog22
nlog2mlog 222 +≤+
≤+
T1 T2
최적화 성능의 수학적 증명
N 개의 노드 가진 트리에서 FindSet 실행 복잡도
O(lo g2N)
단일 노드를 가진 집합들에 대한 N 회의 U n io n
시 시간 복잡도 U n io n 내부에서 Find S e t호출 - > O (lo g
2N)
O (Nlo g2N)
이해하지 못한 정리
트리에서 랭크 알고리즘과 경로압축을
모두 사용할 때 m 회의 MakeSet, FindSet,
UnionSet 을 실행하고 그 중 MakeSet 이
n 회 호출되었다면 시간 복잡도는
O(m log*n) 이다
여기서 log*n = min {k : log log … logn ≤ 1}
즉 , n 에 k 회 거듭 log 를 씌워 1 이하가 나오는
k 의 최소값 -> 어떠한 n 에 대해서도 매우 작은 값
제 한
실용적인 면의 제한 집합 내 순회 불가
리스트의 경우 루트에서부터 순회가 가능하나
트리는 부모로부터 자식으로 순회가 불가한 구조
-> 자식에 대한 참조 배열이나 리스트가 필요
집합 분리에 대해 다루지 않음 집합 일부를 분리하고 합치는 것이 자유로워야
분리집합의 실질적인 활용도가 있을 것으로 보임
끗
감사합니다