레드-블랙 트리(Red-black tree)는 자가 균형 이진 탐색 트리(self-balancing binary search tree)로서, 대표적으로는 연관 배열 등을 구현하는 데 쓰이는 자료구조다. 레드-블랙 트리는 이진 트리의 특수한 형태로서, 컴퓨터 공학 분야에서 숫자 등의 비교 가능한 자료를 정리하는 데 쓰이는 자료구조이다.
Balanced Search Tree로 많이 쓰이는 Red-Black Tree(이하 RB Tree)를 C 언어로 구현.
구현하는 추상 자료형(Abstract Data Type)은 ordered set, multiset.
A red-black tree is a kind of self-balancing binary search tree where each node has an extra bit, and that bit is often interpreted as the color, red or black.
These colors are used to ensure that the tree remains balanced during insertions and deletions.
Rules that every red-black tree follows:
1. Every node has a color either red or black.
2. The root of the tree is always black.
3. There are no adjacent red nodes (A red node cannot have a red parent or red child.
4. Every path from a node (including root) to any of its descendants NULL nodes has the same number of black nodes.
부모 노드의 두 자식 노드가 모두 붉은 노드이면 부모 노드를 붉은 노드로 바꾸고 자식 노드는 검은 노드로 바꿀 수 있다.
부는 노드가 붉은 노드이면 부모 노드를 검은 노드로 바꾸고 자식 노드를 붉은 노드로 바꿀 수 있다.
레프트 로테이션의 핵심은 y의 왼쪽 자식 노드가 x의 오른쪽 자식 노드가 되는 것이다.
x의 오른쪽 자식 노드가 된 y의 부모 노드를 변경한다.
여섯 가지 경우의 수 존재
세 가지 경우는 z의 부모 노드가 왼쪽 자식 노드인 경우, 세 가지는 z의 부모 노드가 오른쪽 자식 노드인 경우
z의 부모 노드가 왼쪽 자식 노드일 때
첫번째 경우는 부모와 삼촌 노드가 모두 붉은 노드
붉은 노드를 위로 이동시킨다. 루트 노드까지 이동하면 검은 노드로 바꿔준다.
두번째 경우는 삼촌 노드가 검은 노드이고 z가 오른쪽 자식 노드인 경우
세번째 경우는 삼촌 노드가 검은 노드이고 z가 왼쪽 자식 노드인 경우
두번째 경우에서 레프트 로테이션을 하면 세번째 경우와 같아진다.
#include <stdio.h>
#include <stdlib.h>
typedef struct node
{
struct node *parent;
struct node *left;
struct node *right;
int data;
int color;
} node;
typedef struct rbtree
{
node *root;
node *NIL;
} rbtree;
node *new_node(int data)
{
node *n = malloc(sizeof(node));
n->parent = NULL;
n->left = NULL;
n->right = NULL;
n->data = data;
n->color = 1;
return n;
}
rbtree *new_rbtree()
{
rbtree *t = malloc(sizeof(rbtree));
node *nil_node = malloc(sizeof(node));
nil_node->parent = NULL;
nil_node->left = NULL;
nil_node->right = NULL;
nil_node->data = 0;
nil_node->color = 0;
t->NIL = nil_node;
t->root = t->NIL;
return t;
}
void left_rotation(rbtree *t, node *x)
{
// x의 오른쪽 자식 노드 y 선언
node *y = x->right;
// y의 왼쪽 자식 노드를 x의 오른쪽 자식 노드로 변경
x->right = y->left;
// y의 왼쪽 자식 노드가 NIL 노드가 아니면 y의 왼쪽 자식 노드의 부모 노드를 x로 변경
if (y->left != t->NIL)
y->left->parent = x;
// y의 부모 노드를 x의 부모 노드로 변경
y->parent = x->parent;
// x의 부모 노드가 NIL 노드이면 트리의 루트 노드를 y로 변경
if (x->parent == t->NIL)
t->root = y;
// x가 부모 노드의 왼쪽 자식 노드이면 x의 부모 노드의 왼쪽 자식 노드를 y로 변경
else if (x == x->parent->left)
x->parent->left = y;
// x가 부모 노드의 오른쪽 자식 노드이면 x의 부모 노드의 오른쪽 자식 노드를 y로 변경
else
x->parent->right = y;
// y의 왼쪽 자식 노드를 x로 변경
y->left = x;
// x의 부모 노드를 y로 변경
x->parent = y;
}
void right_rotation(rbtree *t, node *x)
{
// x의 왼쪽 자식 노드 y 선언
node *y = x->left;
// y의 오른쪽 자식 노드를 x의 왼쪽 자식 노드로 변경
x->left = y->right;
// y의 오른쪽 자식 노드가 NIL 노드가 아니면 y의 오른쪽 자식 노드의 부모 노드를 x로 변경
if (y->right != t->NIL)
y->right->parent = x;
// y의 부모 노드를 x의 부모 노드로 변경
y->parent = x->parent;
// x의 부모 노드가 NIL 노드이면 트리의 루트 노드를 y로 변경
if (x->parent == t->NIL)
t->root = y;
// x가 x의 부모 노드의 왼쪽 자식 노드이면 x의 부모 노드의 왼쪽 자식 노드를 y로 변경
else if (x == x->parent->left)
x->parent->left = y;
// x가 x의 부모 노드의 오른쪽 자식 노드이면 x의 부모 노드의 오른쪽 자식 노드를 y로 변경
else
x->parent->right = y;
// y의 오른쪽 자식 노드를 x로 변경
y->right = x;
// x의 부모 노드를 y로 변경
x->parent = y;
}
void insertion_fixup(rbtree *t, node *z)
{
// z의 부모 노드가 붉은 노드인 경우
while (z->parent->color == 1)
{
// z의 부모 노드가 왼쪽 자식 노드인 경우
if (z->parent == z->parent->parent->left)
{
// z의 오른쫀 삼촌 노드 y 선언
node *y = z->parent->parent->right;
// 삼촌 노드 y가 붉은 노드인 경우
if (y->color == 1)
{
// z의 부모 노드를 검은 노드로 변환
z->parent->color = 0;
// z의 삼촌 노드 y를 검은 노드로 변환
y->color = 0;
// z의 조부모 노드를 붉은 노드로 변환
z->parent->parent->color = 1;
// z를 z의 조부모 노드로 변경
z = z->parent->parent;
}
else
{
// z가 부모 노드의 오른쪽 자식인 경우
if (z == z->parent->right)
{
// z에 z의 부모 노드 저장
z = z->parent;
// 레프트 로테이션 실행
left_rotation(t, z);
}
// z의 부모 노드를 검은 노드로 변환
z->parent->color = 0;
// z의 조부모 노드를 붉은 노드로 변환
z->parent->parent->color = 1;
// 라이트 로테이션 실행
right_rotation(t, z->parent->parent);
}
}
// z의 부모 노드가 오른쪽 자식 노드인 경우
else
{
// z의 왼쪽 삼촌 노드 y 선언
node *y = z->parent->parent->left;
if (y->color == 1)
{
// z의 부모 노드를 검은 노드로 변환
z->parent->color = 0;
// z의 삼촌 노드 y를 검은 노드로 변환
y->color = 0;
// z의 조부모 노드를 붉은 노드로 변환
z->parent->parent->color = 1;
// z를 z의 조부모 노드로 변경
z = z->parent->parent;
}
else
{
// z가 부모 노드의 왼쪽 자식 노드인 경우
if (z == z->parent->left)
{
// z에 z의 부모 노드 저장
z = z->parent;
// 라이트 로테이션
right_rotation(t, z);
}
// z의 부모 노드를 검은 노드로 변환
z->parent->color = 0;
// z의 조부모 노드를 붉으 노드로 변환
z->parent->parent->color = 1;
// 레프트 로테이션 실행
left_rotation(t, z->parent->parent);
}
}
}
// 트리의 루트 노드롤 검은 노드로 변환
t->root->color = 0;
}
void insertion(rbtree *t, node *z)
{
// 트리의 NIL인 노드 y 선언
node *y = t->NIL;
// 트리의 루트 노드 temp 선언
node *temp = t->root;
// temp가 트리의 NIL이 아니면 반복문 실행
while (temp != t->NIL)
{
// y는 temp를 저장
y = temp;
// z의 data가 temp의 data보다 작으면 temp에 temp의 왼쪽 자식 노드 저장
if (z->data < temp->data)
temp = temp->left;
// z의 data가 temp의 data보다 크면 temp에 temp의 오른쪽 자식 노드 저장
else
temp = temp->right;
}
// z의 부모 노드를 y로 변경
z->parent = y;
// y는 트리의 NIL이면 트리의 루트 노드를 z로 변경
if (y == t->NIL)
t->root = z;
// z의 data가 y의 data보다 작으면 y의 왼쪽 자식 노드를 z로 변경
else if (z->data < y->data)
y->left = z;
// z의 data가 y의 data보다 크면 y의 오른쪽 자식 노드를 z로 변경
else
y->right = z;
// z의 왼쪽 자식 노드를 트리의 NIL로 변경
z->left = t->NIL;
// z의 오른쪽 자식 노드를 트리의 NIL로 변경
z->right = t->NIL;
// z의 색깔을 빨강으로 변경
z->color = 1;
insertion_fixup(t, z);
}
void inorder(rbtree *t, node *n)
{
// 노드 n이 트리의 NIL이 아니면 n의 왼쪽 자식 노드를 출력 후 오른쪽 자식 노드를 출력
if (n != t->NIL)
{
inorder(t, n->left);
printf("%d \n", n->data);
inorder(t, n->right);
}
}
int main()
{
// 새로운 레드-블랙 트리 생성
rbtree *t = new_rbtree();
node *a, *b, *c, *d, *e, *f, *g, *h, *i, *j, *k, *l, *m;
a = new_node(10);
b = new_node(20);
c = new_node(30);
d = new_node(100);
e = new_node(90);
f = new_node(40);
g = new_node(50);
h = new_node(60);
i = new_node(70);
j = new_node(80);
k = new_node(150);
l = new_node(110);
m = new_node(120);
insertion(t, a);
insertion(t, b);
insertion(t, c);
insertion(t, d);
insertion(t, e);
insertion(t, f);
insertion(t, g);
insertion(t, h);
insertion(t, i);
insertion(t, j);
insertion(t, k);
insertion(t, l);
insertion(t, m);
inorder(t, t->root);
return 0;
}
좋은 정보 감사합니다