본문으로 건너뛰기
김신건의 로그

트리를 사용한 집합과 맵 (Tree Set / Tree Map)

· 수정 · 📖 약 2분 · 831자/단어 #algorithm #data-structure #tree #set #map #bst
tree set, tree map, ordered set, ordered map, 트리 집합, 트리 맵, 균형 이진 탐색 트리

정의

트리 집합 (Tree Set)트리 맵 (Tree Map) 은 균형 이진 탐색 트리 (Balanced BST) 를 기반으로 한 정렬된 자료구조로, 삽입, 조회, 삭제를 O(log N) 에 처리하며 순서를 보장한다. C++ std::set, std::map (Red-Black Tree), Java TreeSet, TreeMap (Red-Black Tree), Python sortedcontainers.SortedSet 이 대표적이다.

균형 BST는 1962년 Adelson-Velsky와 Landis가 AVL 트리로 처음 고안했으며, 1972년 Bayer가 Red-Black Tree를 제안했다.

문제 상황과 동기

다음 쿼리를 Q번 처리해야 한다:

  • 정렬된 상태 유지: 삽입/삭제 후에도 오름차순 유지
  • 범위 쿼리: “x 이상 최초 원소” (lower_bound), “k번째 작은 원소”
  • 순서 통계: “원소 x의 순위는?”

해시 집합: 평균 O(1) 이지만 순서 없음. 정렬하려면 매번 O(N log N).

트리 집합: O(log N) 삽입/조회/삭제, 순회 시 자동 정렬 O(N).

핵심 통찰: 균형 이진 탐색 트리는 정렬 불변성을 유지하면서 동적 삽입/삭제를 logarithmic time 에 처리한다. 회전 (rotation) 으로 트리 높이를 O(log N) 으로 제한.

실무 위치: DB 인덱스 (B-tree), 운영체제 가상 메모리 관리, 네트워크 라우팅 테이블, 우선순위 큐 대안.

시각화

핵심 아이디어

이진 탐색 트리 (BST)

노드마다:

  • 왼쪽 서브트리 < 노드 < 오른쪽 서브트리

in-order 순회 = 정렬 순서.

문제: 편향 트리 (skewed tree) 는 높이 N, 모든 연산 O(N).

균형 유지

균형 (balanced): 모든 리프의 깊이 차이가 O(log N).

대표 균형 트리:

트리균형 조건회전
AVL모든 노드의 양쪽 서브트리 높이 차 ≤ 1삽입/삭제 시 최대 O(log N)
Red-Black5가지 규칙 (루트 검정, 리프 검정, 빨강 노드는 검정 자식만, 같은 검정 깊이)AVL보다 느슨, 삽입 빠름
B-Tree노드당 여러 키, 디스크 I/O 최적화DB 인덱스 표준

STL std::set = Red-Black Tree. Java TreeSet = Red-Black Tree.

주요 연산

insert(x):
    BST 삽입 후 회전으로 균형 복구
    O(log N)

find(x):
    BST 탐색
    O(log N)

erase(x):
    BST 삭제 후 회전으로 균형 복구
    O(log N)

lower_bound(x):
    x 이상 최초 원소
    O(log N)

order_of_key(x):
    x보다 작은 원소 개수
    O(log N) (augmented tree 필요)

find_by_order(k):
    k번째 작은 원소 (0-indexed)
    O(log N) (augmented tree)

알고리즘

BST 삽입

insert(root, x):
    if root == null:
        return new_node(x)
    if x < root.val:
        root.left = insert(root.left, x)
    else if x > root.val:
        root.right = insert(root.right, x)
    // 균형 복구 (AVL/RB 규칙에 따라)
    return balance(root)

lower_bound (x 이상 최초)

lower_bound(root, x):
    result = null
    while root != null:
        if root.val >= x:
            result = root
            root = root.left
        else:
            root = root.right
    return result

Red-Black Tree 회전 (좌회전 예시)

left_rotate(x):
    y = x.right
    x.right = y.left
    y.left = x
    return y

삽입/삭제 후 최대 2번 회전으로 균형 복구 (RB 특성).

구현

// std::set (Red-Black Tree)
#include <bits/stdc++.h>
using namespace std;
int main() {
  set<int> s;
  s.insert(5); s.insert(3); s.insert(7); s.insert(1);
  
  cout << "sorted: ";
  for (auto x : s) cout << x << " ";
  cout << "\n";
  
  auto it = s.lower_bound(4);
  cout << "lower_bound(4) = " << (it != s.end() ? to_string(*it) : "none") << "\n";
  
  s.erase(3);
  cout << "after erase(3): ";
  for (auto x : s) cout << x << " ";
  cout << "\n";
}
결과
sorted: 1 3 5 7 
lower_bound(4) = 5
after erase(3): 1 5 7

복잡도

항목
삽입O(log N)
조회O(log N)
삭제O(log N)
lower_bound / upper_boundO(log N)
k번째 원소 (augmented)O(log N)
순회 (정렬)O(N)
공간O(N) (노드 포인터 포함)

변형 및 활용

1. Order Statistics Tree (GNU PBDS)

C++ 에서 __gnu_pbds::tree 는 k번째 원소, 순위 쿼리 지원.

#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
using namespace __gnu_pbds;
typedef tree<int, null_type, less<int>, rb_tree_tag, tree_order_statistics_node_update> ordered_set;

ordered_set s;
s.insert(5); s.insert(3); s.insert(7);
cout << *s.find_by_order(1);  // 1번째 (0-indexed) = 5
cout << s.order_of_key(6);    // 6보다 작은 개수 = 2

2. Multiset

중복 허용 트리 집합. std::multiset, Java TreeMultiset.

multiset<int> ms = {3, 3, 5};
cout << ms.count(3);  // 2
ms.erase(ms.find(3)); // 하나만 제거

3. TreeMap (순서 보장 맵)

map<int, string> m;
m[3] = "three"; m[1] = "one";
for (auto [k, v] : m) cout << k << ": " << v << "\n"; // 1: one, 3: three

4. Interval Tree

구간 쿼리 최적화. augmented BST 로 max endpoint 저장.

함정

1. 중복 허용 여부

set 은 중복 무시, multiset 은 허용. 실수로 set.insert() 반복해도 크기 1.

2. iterator 무효화

삭제 후 iterator 무효화. erase(it++) 패턴 사용.

for (auto it = s.begin(); it != s.end(); ) {
    if (*it % 2 == 0) it = s.erase(it);
    else ++it;
}

3. 비교 함수 일관성

커스텀 비교 함수는 strict weak ordering 만족 필요. a < bb < a 동시 true 면 undefined behavior.

4. 해시 대비 느림

단순 존재 여부만 묻는다면 unordered_set 이 평균 2~3배 빠름. 정렬/범위 쿼리 필요할 때만 트리.

BOJ 연습 문제

번호제목정답률링크
BOJ 7662이중 우선순위 큐29.4%kokoa-lab
BOJ 21939문제 추천 시스템 Version 145.2%kokoa-lab
BOJ 2842집배원 한상덕28.7%kokoa-lab
BOJ 1202보석 도둑33.5%kokoa-lab

참고

이 글의 용어 (3개)
집합과 맵 (Set and Map)algorithm
정의 집합 (Set) 은 고유한 원소들의 모임을 나타내는 추상 자료구조로, 멤버십 검사, 삽입, 삭제를 지원한다. 맵 (Map, Dictionary) 은 키-값 쌍을 저장하며, …
해시를 사용한 집합과 맵 (Hash Set / Hash Map)algorithm
정의 해시 집합 (Hash Set) 과 해시 맵 (Hash Map) 은 해시 함수를 이용해 키를 버킷 인덱스로 변환하고, 평균 O(1) 시간에 삽입, 조회, 삭제를 수행하는 자료…
Binary Search Tree (BST): 정렬된 이진 트리algorithm
정의 Binary Search Tree (BST) 는 각 노드가 다음을 만족하는 이진 트리입니다. - 왼쪽 서브트리의 모든 키 < 노드 키 - 오른쪽 서브트리의 모든 키 > 노드…

💬 댓글

사이트 검색 / 명령어

검색

스크롤 = 확대/축소 · 드래그 = 이동 · 0 = 원래 크기 · ESC = 닫기