레드-블랙 트리(Red-Black Tree, RBT)는 균형 잡힌 이진 탐색 트리(BST)의 일종으로, 삽입과 삭제 시 스스로 균형을 유지하여 항상 O(log N) 시간 복잡도를 보장합니다.
기본적인 BST는 삽입 순서에 따라 한쪽으로 치우친 구조가 될 수 있으며, 이 경우 시간 복잡도는 최악의 경우 O(N)까지 증가합니다. 이러한 문제를 해결하기 위해 등장한 것이 바로 자가 균형 트리(Self-Balancing Tree)이며, 그 대표적인 예가 AVL Tree와 레드-블랙 트리입니다.
이 규칙들 덕분에 레드-블랙 트리는 삽입/삭제가 일어나도 트리의 균형이 극단적으로 무너지지 않음을 보장합니다.
enum class Color { Red, Black };
struct Node {
Node* parent = nullptr;
Node* left = nullptr;
Node* right = nullptr;
int key = {};
Color color = Color::Black;
};
key: 노드에 저장된 값color: Red or Blackleft, right, parent: 트리 탐색 및 회전에서 핵심이 되는 포인터또한, 모든 자식이 없는 노드는 NIL 노드라는 더미 노드를 가리키며, 이는 항상 Black입니다.
InsertFixup)void InsertFixup(Node* node) {
while (node->parent->color == Color::Red) {
Node* uncle = (node->parent == node->parent->parent->left)
? node->parent->parent->right
: node->parent->parent->left;
if (uncle->color == Color::Red) {
// Case 1: 부모와 삼촌이 Red
node->parent->color = Color::Black;
uncle->color = Color::Black;
node->parent->parent->color = Color::Red;
node = node->parent->parent;
}
else {
if (node == node->parent->right && node->parent == node->parent->parent->left) {
// Case 2: Triangle (왼쪽-오른쪽)
node = node->parent;
LeftRotate(node);
}
else if (node == node->parent->left && node->parent == node->parent->parent->right) {
// Case 2 (대칭): 오른쪽-왼쪽
node = node->parent;
RightRotate(node);
}
// Case 3: Line
node->parent->color = Color::Black;
node->parent->parent->color = Color::Red;
if (node == node->parent->left)
RightRotate(node->parent->parent);
else
LeftRotate(node->parent->parent);
}
}
_root->color = Color::Black; // 루트는 항상 Black
}
void LeftRotate(Node* x) {
Node* y = x->right;
x->right = y->left;
if (y->left != _nil) y->left->parent = x;
y->parent = x->parent;
if (x->parent == _nil)
_root = y;
else if (x == x->parent->left)
x->parent->left = y;
else
x->parent->right = y;
y->left = x;
x->parent = y;
}
→ RightRotate는 반대 방향으로 동일하게 작동
DeleteFixup 함수를 통해 균형 복구Black 노드를 삭제하거나 대체했을 때, 트리의 한 경로에 Black이 하나 부족해진 상태를 말합니다.
DeleteFixup)void DeleteFixup(Node* x) {
while (x != _root && x->color == Color::Black) {
if (x == x->parent->left) {
Node* s = x->parent->right;
if (s->color == Color::Red) {
// Case 1: 형제가 Red
s->color = Color::Black;
x->parent->color = Color::Red;
LeftRotate(x->parent);
s = x->parent->right;
}
if (s->left->color == Color::Black && s->right->color == Color::Black) {
// Case 2: 형제와 자식들 모두 Black
s->color = Color::Red;
x = x->parent;
}
else {
if (s->right->color == Color::Black) {
// Case 3: 형제의 Near Child가 Red
s->left->color = Color::Black;
s->color = Color::Red;
RightRotate(s);
s = x->parent->right;
}
// Case 4: 형제의 Far Child가 Red
s->color = x->parent->color;
x->parent->color = Color::Black;
s->right->color = Color::Black;
LeftRotate(x->parent);
x = _root;
}
}
else {
// 대칭적인 오른쪽 자식 케이스
// 동일한 로직, 방향만 반대로 작성
}
}
x->color = Color::Black;
}
| 구분 | 레드-블랙 트리 | AVL 트리 |
|---|---|---|
| 삽입/삭제 성능 | 빠름 (덜 엄격한 균형) | 느림 (균형 유지가 까다로움) |
| 탐색 성능 | 보통 | 더 빠름 |
| 회전 횟수 | 적음 | 많음 |
| 사용 예 | STL map, set | 고속 탐색이 필요한 경우 |