동기
균형 이진 탐색 트리는 보통 회전 규칙(AVL, Red-Black)으로 높이를 \(O(\log n)\) 으로 유지한다. 스플레이 트리는 명시적 균형 정보를 두지 않고, 접근한 노드를 매번 루트로 끌어올리는 스플레이 연산 하나로 amortized \(O(\log n)\) 을 달성한다. 단순하면서도 구간 뒤집기·구간 합 같은 시퀀스 연산까지 자연스럽게 확장된다.
스플레이 연산
노드 \(x\) 를 루트로 올리는 회전 묶음. 부모 \(p\), 조부모 \(g\) 의 모양에 따라 세 경우:
- zig (\(p\) 가 루트): \(x\) 와 \(p\) 를 한 번 회전.
- zig-zig (\(x,p\) 가 같은 쪽 자식): 먼저 \(p\)–\(g\) 회전, 다음 \(x\)–\(p\) 회전.
- zig-zag (반대쪽): \(x\)–\(p\), 이어서 \(x\)–\(g\) 회전.
zig-zig를 "조부모 먼저" 돌리는 것이 핵심으로, 이것이 경로를 절반으로 접어 amortized 보장을 만든다.
Amortized 분석 (포텐셜)
노드 \(v\) 의 크기를 \(s(v)\), 랭크 \(r(v)=\log_2 s(v)\), 포텐셜 \(\Phi = \sum_v r(v)\) 로 정의한다. 한 번의 스플레이 비용(회전 수)은
$$ \text{amortized cost} \le 3\big(r(\text{root}) - r(x)\big) + 1 = O(\log n) $$
임이 Access Lemma로 증명된다. 따라서 \(m\) 번 연산의 총비용은 \(O((m+n)\log n)\).
시퀀스로서의 스플레이 트리
원소를 중위 순회 순서 = 배열 인덱스로 보면, 스플레이 트리는 동적 배열이 된다. \(k\)번째 원소 찾기는 서브트리 크기로 내려가고, \([l,r]\) 구간을 다루려면 그 구간을 한 서브트리로 고립(splay 2번) 시킨다: \(l-1\) 번째를 루트로, \(r+1\) 번째를 그 오른쪽 자식 루트로 올리면 가운데 서브트리가 정확히 \([l,r]\).
lazy 전파
구간 뒤집기는 노드에 rev 플래그를 두고, 내려갈 때 자식 좌우를 swap하며 전파한다. 구간 덧셈/합도 동일한 lazy 패턴. 회전 전에는 반드시 push_down, 회전 후에는 pull_up(크기/합 재계산).
비교
| 구조 | 균형 | 구간 뒤집기 | 비고 |
|---|---|---|---|
| 스플레이 | amort. \(O(\log n)\) | 가능 | 단순, 상수 다소 큼 |
| Treap | 기대 \(O(\log n)\) | 가능(implicit) | 난수 의존 |
| Red-Black | 최악 \(O(\log n)\) | 어려움 | STL map 내부 |