Treap
2015. 7. 8. 01:06ㆍ알고리즘 문제풀기/자료구조
Tree와 Heap의 합성어인 Treap은 확률적인 BST이다.
Red-Black Tree, AVL Tree 등은 모든 연산에서 O(log N)의 시간복잡도를 보인다.
Splay Tree 등은 평균적으로, amortized O(log N)의 시간복잡도를 보인다.
Treap은 평균적으로 O(log N)이나, 최악의 경우 O(N)의 시간복잡도를 가진다.
이는 Problem Solving에서 생각보다 유용한데, 대부분의 데이터는 랜덤하게 만들기 때문에 최악의 경우가 나오기 쉽지 않다.
또한 이러한 Treap이나, 문자열의 hashing같은 경우 푸는 사람들이 사용할 소수나 알고리즘을 예측할 수 없기 때문에 임의로 최악의 경우를 만들기도 힘들다.
모든 노드에 대해 자신의 priority는 자식들의 priority보다 작다는 heap의 성질을 만족한다. 삽입/삭제 시 이러한 조건이 만족되도록 노드들을 회전(rotation)해주는 과정이 핵심이다.
이러한 우선순위를 정할 때, 순서대로 1,2,3 이런 식으로 주면 최악의 경우 O(N)이 될 수 있기 때문에, pseudo-random하게 정해주면 높은 확률로 O(lg N)이 된다.
아래는 구현체이다. node에서 부모로 향하는 포인터를 일부러 주지 않고, 재귀로 구현해보았다. 자식 노드가 바뀌는 일을 처리하기 위해 chf라는 변수를 정의하여, '이 노드가 있던 위치에는 새로운 노드가 와야 한다'라는 내용을 지정했다. 같은 원소가 여러 개 삽입될 수 있는지, 그 때의 lower bound는 어떻게 되는지, 아직 불확실하게 구현하긴 했지만, 전체적으로 이런 느낌이구나 하는 것은 알 수 있었다.
모든 노드에 대해 자신의 priority는 자식들의 priority보다 작다는 heap의 성질을 만족한다. 삽입/삭제 시 이러한 조건이 만족되도록 노드들을 회전(rotation)해주는 과정이 핵심이다.
이러한 우선순위를 정할 때, 순서대로 1,2,3 이런 식으로 주면 최악의 경우 O(N)이 될 수 있기 때문에, pseudo-random하게 정해주면 높은 확률로 O(lg N)이 된다.
아래는 구현체이다. node에서 부모로 향하는 포인터를 일부러 주지 않고, 재귀로 구현해보았다. 자식 노드가 바뀌는 일을 처리하기 위해 chf라는 변수를 정의하여, '이 노드가 있던 위치에는 새로운 노드가 와야 한다'라는 내용을 지정했다. 같은 원소가 여러 개 삽입될 수 있는지, 그 때의 lower bound는 어떻게 되는지, 아직 불확실하게 구현하긴 했지만, 전체적으로 이런 느낌이구나 하는 것은 알 수 있었다.
template<typename T> struct node { T value; int pr; node *l, *r; bool chf; node *newnode; node(T v){ value=v; pr=nextP(); l=r=0; chf=false; } ~node(){ if(l) delete l; if(r) delete r; } }; template<typename T> struct bst { node<T> *root; bst(){ root=0; } void insert(T x,node<T> *pos){ if(pos->value <= x){ if(pos->r){ insert(x,pos->r); if(pos->r->chf) pos->r->chf=false, pos->r=pos->r->newnode; } else { pos->r=new node<T>(x); } if(pos->pr > pos->r->pr){ node<T>* np=pos->r; pos->r=np->l; np->l=pos; pos->chf=true; pos->newnode=np; } } else { if(pos->l){ insert(x,pos->l); if(pos->l->chf) pos->l->chf=false, pos->l=pos->l->newnode; } else { pos->l=new node<T>(x); } if(pos->pr > pos->l->pr){ node<T>* np=pos->l; pos->l=np->l; np->l=pos; pos->chf=true; pos->newnode=np; } } } void insert(T x){ if(root==0) root=new node<T>(x); else { insert(x,root); if(root->chf){ root->chf=false; root=root->newnode; } } } ~bst(){ delete root; } node<T>* lower_bound(T x,node<T>* pos){ if(pos->value <= x){ if(pos->r == 0) return 0; else { return lower_bound(x,pos->r); } } else { if(pos->l == 0) return pos; else { node<T> *tmp = lower_bound(x,pos->l); if(tmp) return tmp; else return pos; } } } node<T>* lower_bound(T x){ return lower_bound(x,root); } };
R-B tree, AVL tree와 비교되는 장점으로는 구현이 간단하다는 것이 있다.
직접 구현해보니 재밌었다. Splay Tree도 다음에 구현해봐야지.
'알고리즘 문제풀기 > 자료구조' 카테고리의 다른 글
Sparse Table (8) | 2020.03.02 |
---|---|
구간의 합 구하기 (0) | 2018.07.27 |
Binary Indexed Tree와 구간의 사용방법 (0) | 2013.09.06 |
Fenwick tree (0) | 2013.09.02 |