1. 디스조인트-셋 자료구조란?


disjoint-set / union-find / merge-find 등등으로 불린다.


이름만 봐도 눈치깔 수 있드시, 서로소(disjoint)인 집합들을 가지고 합칠 수도 있고(union, merge) 원소를 찾는 등의

집합질 특화된 자료구조다.


우리 위키를 참조하자면, 이 자료구조는 여러 서로소(원소가 중복되지 않음) 부분집합들로 이루어지고,


새로운 집합을 추가하거나, 기존의 집합과 합치거나, 집합에 어떤 원소가 들어가 있는지를 찾는 연산을


거의 선형시간에 가깝게 수행하는 자료구조다!



2. 어디에 적용시킬까?


당연히 집합과 관련된(파티션질이 필요한) 문제에 써야한다.


우리는 이미 집합연산을 제공하는 stl을 알고 있다 set헤더에서 제공하는 set자료구조가 그것인데,


이 set 컨테이너는 정말 집합처럼 작동한다.


집합의 크기, 어떤 원소를 갖고 있는지 아닌지, 집합끼리의 비교도 가능하다.


이 컨테이너는 노드 기반 컨테이너이며 균형 이진트리로 구축된다.

따라서 찾기연산과 삽입연산은 로그 시간의 복잡도를 가진다고 한다.


디스조인트-셋 자료구조의 강점은 set 컨테이너보다 빠른 시간에 있다.


하지만 세상에 공짜는 없는법, 이 자료구조도 특성상 몇가지 결점이 있다.


작동원리를 세부적으로 보면서 알아보자.


1) makeset 연산


새로운 한 원소를 대표로 하는 집합을 만드는 연산이다.

function MakeSet(x) add x to the disjoint-set tree x.parent := x

parent는 x의 부모로, 계속해서 parent를 찾아가서 x가 속한 집합의 대표를 찾는데 사용한다.


2) find 연산


이 연산은 x를 포함하는 집합의 대표원소를 찾는 연산이다.


function Find(x)
   if x.parent != x
     x.parent := Find(x.parent)
   return x.parent

x부터 시작하여 계속 parent를 찾아 재귀적으로 다시 find를 호출하는 모습을 볼 수 있다.

마지막엔 자기자신을 부모로 갖고있는 원소가 리턴되는데 이 원소가 바로 집합의 대표원소다.


만약 집합 {1}과 {3}이 있었는데, 전자를 후자에 합쳤다고 해보자. {3, {1}} 그러면 이 집합의 대표는 3이다.

find(1) = 3. find(3) = 3.


3) union 연산


합집합을 담당하는 연산이다.


두 원소를 매개변수로 하고, 각각의 대표들을 찾아 하나로 통일하는 과정을 거친다.


function Union(x, y) xRoot := Find(x) yRoot := Find(y) xRoot.parent := yRoot

여기서는 x가 포함된 집합을 y가 포함된 집합에 합치는 모습을 볼 수 있다.


의사코드를 봐도 매우 간단한 모습을 보인다.


set은 이진트리의 구조를 가지지만, 디스조인트-셋은 트리처럼 동작하는 parent 배열만 갖고 있어도 잘 동작한다.

이 점에서 속도의 장점을 얻는다.


위대하신 위키께서 이 과정을 그림으로 잘 설명해주셨다.



다섯 가지의 원소들로 집합질을 하는 과정을 잘 보여준다.


단점은 그래프구조를 모방한 구조에서 비롯된다. 대표자(루트노드)만을 내새운 방법 때문에, 다른 노드끼리의 그래프질은 불가능하다.


실제로 구현해보면 알겠지만, 대표번호만 맞을 뿐 속의 노드관계는 우리가 상상한 그래프와는 전혀 다른 모습을 가진다.

(비슷하게 하려면 더욱 개선된 디스조인트 연산들이 필요하다)


따라서 이 점 때문에 각 집합의 대표자만 연관된 문제들에 대해 이 자료구조를 적용할 수 있을 것이다.


나중에 최소신장트리를 구성하는데 이 자료구조를 활용하기도 한다. (아직 잘 모르니까 소개는 이정도로 ㅎㅎ)



2-1) 적용문제 1 : 집합의 표현

https://www.acmicpc.net/problem/1717


문제 제목부터 집합을 표현할 자료구조를 요구하고 있다.


{0}, {1}, {2}, {3}, ... {n} 의 부분집합들이 있다. 집합들을 합치기 전후로, 두 원소가 주어질 때, 두 원소가 같은 집합에 있는지 구해야 한다.


0~n까지의 부모번호를 저장하는 parent[] 배열을 준비한다. 각 원소는 자기자신의 번호로 초기화한다.


합집합 (0 a b)을 요구하면 union연산 (O(α(n)) = O(1)을,

같은 집합인지 알아보는 연산은(1 a b) 각각의 부모를 찾아( find(a), find(b) = (O(α(n)) = O(1))) 같은 부모(대표)인지 보면 된다.


코드 : http://js1jj2sk3.tistory.com/16?category=728064



2-2) 적용문제 2 : 여행 가자

https://www.acmicpc.net/problem/1976


그래프질이 필요한 문제다.


n개의 도시들이 주어지고, 각 도시를 잇는 도로가 있다.

여행계획이 주어질 때, 도로를 따라 계획대로 전부 방문할 수 있는지 없는지 판별해야 한다.


대충 생각해보자면, 여행계획대로 순서대로 실제로 그래프를 순회해보면서 방문가능한지를 계속 판별해야 할 것같다.


하지만 단순히 생각해보자면, 어쨌든 방문만 하면 되므로,

여행계획에 포함된 도시들이 같은 집합이기만 하면(신장트리라면) 방문 할 수 있다고 판별할 수 있다.


따라서 1번 도시와 2번도시가 연결되어 있다면 union(1, 2)으로 묶어주고(O(α(n)) = O(1)

여행계획의 도시들이 같은 집합인지 find()로 판별한다.(O(α(n))= O(1))


코드 : http://js1jj2sk3.tistory.com/17?category=728064


'알고리즘 > Disjoint-set 디스조인트-셋' 카테고리의 다른 글

백준) 10775 공항  (0) 2017.07.29
백준) 9938 방 청소  (0) 2017.07.29
백준) 4195 친구 네트워크  (0) 2017.07.28
백준) 1976 여행 가자  (0) 2017.07.28
백준) 1717 집합의 표현  (0) 2017.07.28

+ Recent posts