본문 바로가기

자료구조/Link Cut Tree

[Link Cut Tree] 1. 개요와 구현

먼저 알아야 할 것 : Splay Tree


Link Cut Tree(LCT)는 Forest를 관리하는 자료구조이며, 각각의 트리는 루트를 가집니다.

LCT는 이름에도 있듯 트리 두개를 연결하고(Link), 끊는(Cut) 연산을 가지고 있습니다.

LCT에서는 마치 Heavy-Light Decomposition처럼 트리를 몇 개의 Path로 나누어 관리하는데, 각각의 Path를 관리할 때 주로 Splay Tree를 사용합니다.

       


왼쪽 트리가 실제 표현하고자 하는 트리의 모습이고, 오른쪽 트리는 왼쪽 트리를 Link Cut Tree로 구현한 모습입니다.

실선으로 연결된 경로가 하나의 Path이고, 점선은 Path의 루트가 Path의 부모를 가리키도록 한 선입니다.

왼쪽 트리에서 (1, 2, 5, 9), (3, 7), (6, 10), (4), (8) 5개의 경로로 나뉘었는데, 각각의 경로는 깊이 순서로 Binary Search Tree가 되었고, 각 BST의 루트의 Parent 포인터는 해당 Path의 부모 노드를 가리키고 있는데, 반대 방향으로는 가리키고 있지 않습니다.

Path를 나누는 것이 HLD와 조금 다른데, HLD에서는 Heavy Path가 고정되지만, LCT에서는 Path가 동적으로 변화합니다.

LCT는 이 Path들과 Splay Tree를 이용해서 트리를 동적으로 연결하고 끊으며 다양한 연산을 하는 자료구조입니다.



struct node { node *l, *r, *p; int cnt; node() : l(0), r(0), p(0), cnt(0) {} };


Link Cut Tree에서 node는 기본적으로 Splay Tree와 같으며, 자식과 부모의 포인터만이 필요합니다.

이 글에서는 노드의 Depth를 구하기 위해 Subtree의 노드 개수를 저장하는 cnt 변수를 사용하였습니다.


bool isRoot(node * x) {
    return (x->p == 0 || (x->p->l != x && x->p->r != x));
}


LCT에서는 Splay Tree의 루트의 Parent가 NULL이 아닐 수 있기 때문에 해당 노드가 Splay Tree의 루트인지 확인하는 함수를 만듭니다.

x->p가 NULL이면 당연히 x가 루트입니다.

x->p가 NULL이 아닌데 x가 x->p의 자식이 아니라면 x와 x->p는 서로 다른 Splay Tree에 속해 있는 것이고, x가 해당 Splay Tree의 루트인 것이 됩니다. 기존 Splay Tree의 구현에서 x가 루트인지 확인하는 부분을 모두 isRoot로 바꿔줍니다.



위 코드들은 Splay Tree에서 사용하는 것과 같으므로 설명하지 않습니다.


node * access(node * x) {
    splay(x);
    x->r = 0;
    update(x);

    node * ret = x;
    while (x->p) {
        node * y = x->p;
        ret = y;
        splay(y);
        y->r = x;
        update(y);
        splay(x);
    }
    return ret;
}


LCT의 하이라이트(?)인 Access 연산입니다. LCT에서 많은 연산들을 처리할 때 Access를 거칩니다.

이 연산이 amortized $O(logn)$ 시간복잡도를 가지기 때문에 Link, Cut 등의 연산들이 모두 amortized $O(logn)$의 시간복잡도를 가지게 됩니다.


Access 연산이 하는 일은 x와 트리의 루트 사이의 경로를 하나의 Path로 연결하고, 이 Path의 Splay Tree에서 x를 루트로 만드는 연산입니다. Splay Tree의 Splay와 비슷하다고 볼 수 있습니다.


코드를 자세히 보면, 시작할 때 x를 Splay하고, x의 오른쪽 자식을 모두 떼어냅니다.

떼어진 자식들은 x보다 깊이가 큰 노드들로, 트리에서 x의 자식들을 모두 떼어낸 것입니다.

그리고 x->p가 NULL이 아니라면, x를 포함하는 Path에 부모 노드 y가 존재한다는 것을 의미합니다.

따라서 y의 오른쪽에 x를 붙여서 x가 포함된 Path를 모두 y와 같은 Path로 연결시킵니다.

이것을 x->p가 NULL일 때까지 반복하면 x와 트리의 루트가 하나의 Path로 묶이게 됩니다.


참고로 Access가 끝난 후 반환하는 값은 Access 전에 '루트가 포함되던 Path'에서 x와 가장 가까이 있던 노드입니다.


Access를 이용해서 뭘 할 수 있는지는 다음 글에서..

'자료구조 > Link Cut Tree' 카테고리의 다른 글

[Link Cut Tree] 6. 간선 쿼리  (0) 2017.11.30
[Link Cut Tree] 5. 구간 갱신(Lazy Propagation)  (2) 2017.09.08
[Link Cut Tree] 4. 쿼리  (3) 2017.09.07
[Link Cut Tree] 3. 연산 (2)  (1) 2017.09.06
[Link Cut Tree] 2. 연산 (1)  (3) 2017.08.29