레드-블랙 트리
2-3-4트리 역할이 가능한 이진 탐색 트리
레드 블랙 트리 테스트 예시 (전개도 참고)
레드-블랙 트리 삽입 연산 구현
#include <stdio.h> #include <stdlib.h> #define nil -99999 typedef struct TreeNode { int key; int color; // red:0, black:1 int root; // 루트 노드 파악 int size_field; struct TreeNode* left, * right; }TreeNode; typedef struct TreeNode* TreeNode_ptr; // (현재 서브트리의 루트, 새로 삽입한 노드) typedef struct set { TreeNode_ptr T; TreeNode_ptr new_node; }set; typedef struct set* set_ptr; // 루트 노드 생성 (키 값은 초기화하지 않음) TreeNode_ptr init() { TreeNode_ptr T = (TreeNode_ptr)malloc(sizeof(TreeNode)); T->root = 1; // 루트 표시 T->color = 1; // 루트의 색은 블랙 // nil 처리 T->left = (TreeNode_ptr)malloc(sizeof(TreeNode)); T->right = (TreeNode_ptr)malloc(sizeof(TreeNode)); T->left->key = T->right->key = nil; T->left->root = T->right->root = 0; T->left->color = T->right->color = 1; // nil은 항상 블랙 return T; } // 트리에 size_field(부분트리에 속한 노드의 개수) 부여 // postorder를 응용해서 제작함 void get_size_field(TreeNode_ptr T) { if (T->key != nil) { get_size_field(T->left); get_size_field(T->right); if ((T->left->key == nil) && (T->right->key == nil)) // 잎 노드 T->size_field = 1; else { // 내부 노드 if ((T->left->key != nil) && (T->right->key != nil)) T->size_field = 1 + (T->left->size_field) + (T->right->size_field); else if ((T->left->key != nil) && (T->right->key == nil)) T->size_field = 1 + (T->left->size_field); else T->size_field = 1 + (T->right->size_field); } } } // 이진 탐색 트리의 부모 탐색 (루트가 오면 NULL 반환, nil노드가 오면 nil노드 반환) // 순환 호출할 때 현재 노드를 같이 넘겨주면, 부모 노드를 받는 것이 됨 -> 값이 같으면 부모 노드 리턴 가능! TreeNode_ptr search_parent_node(TreeNode_ptr T, int key, TreeNode_ptr parent) { if (T == NULL) return NULL; if (T->key == nil) return T; if (key == T->key) return parent; else if (key < T->key) return search_parent_node(T->left, key, T); else return search_parent_node(T->right, key, T); } // 이진 탐색 트리 전용 노드 생성 TreeNode_ptr make_node(int key) { TreeNode_ptr tmp = (TreeNode_ptr)malloc(sizeof(TreeNode)); tmp->key = key; tmp->color = 0; tmp->root = 0; // nil 처리 tmp->left = (TreeNode_ptr)malloc(sizeof(TreeNode)); tmp->right = (TreeNode_ptr)malloc(sizeof(TreeNode)); tmp->left->key = tmp->right->key = nil; tmp->left->root = tmp->right->root = 0; tmp->left->color = tmp->right->color = 1; // nil은 항상 블랙 return tmp; } // 이진 탐색 트리 삽입 (삽입한 노드를 반환) set_ptr insert_node(TreeNode_ptr T, int key, set_ptr s) { static int first = 1; TreeNode_ptr node = NULL; // 삽입할 노드 // 첫 번째 시도 <루트 노드 처리> if ((first--) == 1) { T->key = key; s->T = s->new_node = T; return s; } // n번째 시도 (n>1) if (T->key == nil) { node = make_node(key); s->T = s->new_node = node; return s; } if (key < T->key) { s = insert_node(T->left, key, s); T->left = s->T; s->T = T; return s; } else if (key > T->key) { s = insert_node(T->right, key, s); T->right = s->T; s->T = T; return s; } else { // 동일한 키는 허용하지 않음 s->T = s->new_node = T; return s; } } // x의 부모 노드를 반환 TreeNode_ptr get_parent(TreeNode_ptr T, TreeNode_ptr x) { return search_parent_node(T, x->key, NULL); // x가 루트면 NULL 반환 } // x의 조부모 노드를 반환 TreeNode_ptr get_Gparent(TreeNode_ptr T, TreeNode_ptr x, TreeNode_ptr x_parent) { TreeNode_ptr x_Gparent = NULL; if (x_parent) x_Gparent = search_parent_node(T, x_parent->key, NULL); return x_Gparent; } // x의 삼촌 노드를 반환 TreeNode_ptr get_uncle(TreeNode_ptr T, TreeNode_ptr x, TreeNode_ptr x_parent, TreeNode_ptr x_Gparent) { TreeNode_ptr x_uncle = NULL; if (x_Gparent) { x_uncle = x_Gparent->left; if (x_uncle == x_parent) x_uncle = x_Gparent->right; } return x_uncle; } // 지그재그 판별기 int zig_zag(TreeNode_ptr gp, TreeNode_ptr p, TreeNode_ptr x) { // [1] x가 왼쪽 자식인데 x_parent가 오른쪽 자식 if (p->left == x && gp->right == p) return 1; // [2] x가 오른쪽 자식인데 x_parent가 왼쪽 자식 else if (p->right == x && gp->left == p) return 2; else return 0; } // 지그지그 판별기 int zig_zig(TreeNode_ptr gp, TreeNode_ptr p, TreeNode_ptr x) { // [3] x가 왼쪽 자식인데 x_parent도 왼쪽 자식 if (p->left == x && gp->left == p) return 3; // [4] x가 오른쪽 자식인데 x_parent도 오른쪽 자식 else if (p->right == x && gp->right == p) return 4; else return 0; } // rotate_right // RL과 LL인 상황에 쓰임 TreeNode_ptr rotate_right(TreeNode_ptr parent) { TreeNode_ptr child = parent->left; parent->left = child->right; // child의 서브 트리 한 쪽을 parent가 가져감 child->right = parent; // 오른쪽 회전 return child; } // rotate_left // LR과 RR인 상황에 쓰임 TreeNode_ptr rotate_left(TreeNode_ptr parent) { TreeNode_ptr child = parent->right; parent->right = child->left; // child의 서브 트리 한 쪽을 parent가 가져감 child->left = parent; // 왼쪽 회전 return child; } // 레드 블랙 트리 삽입 TreeNode_ptr rbt_insert_node(TreeNode_ptr T, int key, set_ptr s) { // 이진 탐색 트리에 key값을 가지는 노드 삽입 -> 삽입한 노드를 x로 받음 TreeNode_ptr x = insert_node(T, key, s)->new_node; // x노드의 부모 노드와 조부모 노드, 삼촌 노드를 정의 TreeNode_ptr x_parent = get_parent(T, x); TreeNode_ptr x_Gparent = get_Gparent(T, x, x_parent); TreeNode_ptr x_uncle = get_uncle(T, x, x_parent, x_Gparent); x->color = 0; // x의 색은 레드 // x가 루트 노드가 아니고, 부모 노드의 색이 레드이면 while (!x->root && x_parent->color == 0) { // x의 삼촌 노드가 존재하고 if (x_uncle) { // <경우1> 삼촌 노드가 레드인 상황 if (x_uncle->color == 0) { x_Gparent->color = 0; // Gparent를 레드 x_parent->color = x_uncle->color = 1; // parent와 uncle을 블랙 x = x_Gparent; // x노드의 부모 노드와 조부모 노드, 삼촌 노드를 재 정의 x_parent = get_parent(T, x); x_Gparent = get_Gparent(T, x, x_parent); x_uncle = get_uncle(T, x, x_parent, x_Gparent); } else { int tmp = 0; T->root = 0; // <경우2> zig-zag인 상황: [1] (x가 왼쪽 자식인데 x_parent가 오른쪽 자식) or [2] (x가 오른쪽 자식인데 x_parent가 왼쪽 자식) if (tmp = zig_zag(x_Gparent, x_parent, x)) { // [1] RL인 상황 -> rotate_right 진행 if (tmp == 1) x_Gparent->right = rotate_right(x_parent); // [2] LR인 상황 -> rotate_left 진행 else x_Gparent->left = rotate_left(x_parent); x = x_parent; // x노드의 부모 노드와 조부모 노드, 삼촌 노드를 재 정의 x_parent = get_parent(T, x); x_Gparent = get_Gparent(T, x, x_parent); x_uncle = get_uncle(T, x, x_parent, x_Gparent); } // <경우3> zig-zig인 상황: [3] (x가 왼쪽 자식인데 x_parent도 왼쪽 자식) or [4] (x가 오른쪽 자식인데 x_parent도 오른쪽 자식) tmp = zig_zig(x_Gparent, x_parent, x); TreeNode_ptr x_GGParent = search_parent_node(T, x_Gparent->key, NULL); // [3] LL인 상황 -> rotate_right 진행 if (tmp == 3) { // 조부모의 부모 노드가 존재하면 회전 후 받은 루트를 해당 노드에 연결 if (x_GGParent) { if (x_GGParent->right == x_Gparent) x_GGParent->right = rotate_right(x_Gparent); else x_GGParent->left = rotate_right(x_Gparent); } // 조부모의 부모 노드가 존재하지 않으면 회전 후 받은 루트를 트리의 루트로 지정 else T = rotate_right(x_Gparent); } // [4] RR인 상황 -> rotate_left 진행 else { if (x_GGParent) { // 조부모의 부모 노드가 존재하면 회전 후 받은 루트를 해당 노드에 연결 if (x_GGParent->right == x_Gparent) x_GGParent->right = rotate_left(x_Gparent); else x_GGParent->left = rotate_left(x_Gparent); } // 조부모의 부모 노드가 존재하지 않으면 회전 후 받은 루트를 트리의 루트로 지정 else T = rotate_left(x_Gparent); } x_Gparent->color = 0; // Gparent노드 레드 x_parent->color = 1; // parent노드 블랙 T->root = 1; break; } } } T->color = 1; // 루트 노드의 색은 블랙 return T; } // 중위 순회 (정렬) void inorder(TreeNode_ptr T) { if (T->key != nil) { inorder(T->left); printf("[ key: %d, ", T->key); printf("root: %d, ", T->root); printf("size_field: %d, ", T->size_field); if (T->color == 0) printf("color: red ]\n"); else printf("color: black ]\n"); inorder(T->right); } } // 레드 블랙 트리 연산 테스트 int main() { // 세트 변수 s 정의 : 노드를 삽입했을 때, 삽입한 노드를 반환받기 위해 필요한 변수 set s; TreeNode_ptr T = init(); // 삽입 테스트 T = rbt_insert_node(T, 5, &s); T = rbt_insert_node(T, 3, &s); T = rbt_insert_node(T, 20, &s); T = rbt_insert_node(T, 15, &s); T = rbt_insert_node(T, 25, &s); T = rbt_insert_node(T, 13, &s); T = rbt_insert_node(T, 18, &s); T = rbt_insert_node(T, 30, &s); T = rbt_insert_node(T, 17, &s); T = rbt_insert_node(T, 31, &s); T = rbt_insert_node(T, 33, &s); T = rbt_insert_node(T, 32, &s); T = rbt_insert_node(T, 34, &s); // 사이즈 필드 값 부여 get_size_field(T); // 중위 순회하면서 검토 inorder(T); return 0; }