알고리즘 공부/Binary Tree

레드-블랙 트리

환성 2022. 1. 4. 11:12
728x90

레드-블랙 트리란?

 

각각의 노드가 레드블랙인 색상 속성을 가지고 있는 이진 탐색 트리

이진 탐색 트리가 가지는 일반적인 조건에 다음과 같은 추가적인 조건을 만족해야 유효한 레드-블랙 트리이다.

  1. 노드는 레드 또는 블랙
  2. 루트 노드는 블랙
  3. 모든 리프 노드는 블랙
  4. 레드 노드의 자식은 모두 블랙 → 루트부터 리프 노드까지의 경로에 레드가 연이어 올 수 없다
  5. 어떤 노드에서 리프 노드까지의 모든 경로에는 같은 수의 블랙 노드가 존재

위 조건으로부터, 루트로부터 가장 긴 경로가 가장 짧은 경로의 길이의 두 배를 넘지 않는다. 이를 개략적으로 균형이 잡혀 있다(roughly balanced)고 한다.

→ 최악의 시간복잡도가 트리의 높이(깊이)에 따라 결정되므로 보통의 이진트리보다 효율적

 

💡 4와 5번째 규칙에 의해 최단거리의 경우 모두 블랙노드가, 최장의 경우 레드와 블랙노드가 교차하는 식으로 구성된다. 따라서 최장 거리가 최단거리의 두배를 넘을 수 없다.

 

 

시간복잡도

레드-블랙 트리 최악
삽입 O(logN)
탐색 O(logN)
삭제 O(logN)

 

 

회전

레드-블랙 트리에 새로운 값이 삽입되거나 삭제가 되었을 때

→ 레드-블랙 트리의 특징이 깨지게 되는 경우가 존재

→ 회전연산을 통해 조건을 충족

 

우회전

  1. 루트노드가 13에서 10으로 변경 → 13이 10의 자식노드로 변경
  2. 12가 13의 왼쪽 자식 노드로 옮겨짐

 

좌회전

  1. 루트노드가 10에서 13으로 변경 → 10이 13의 자식노드로 변경
  2. 12가 10의 오른쪽 자식 노드로 옮겨짐

 

삽입연산

기본적으로 레드-블랙트리의 삽입은 이진탐색트리와 동일하지만 삽입 이후에 규칙에 맞춰 트리를 복구하는 과정을 거친다.

  1. 모든 노드는 빨간색 아니면 검은색 → 문제없음
  2. 루트노드는 검은색 → 루트노드가 빨간색인 경우 다시 검은색으로 수정
  3. 리프노드는 검은색 → 문제없음
  4. 빨간 노드의 자식들은 모두 검은색
    • 하지만 검은색 노드의 자식은 빨간색일 필요 없음 → 문제 발생
  5. 루트노드에서 리프노드까지 사이에 있는 검은색 노드의 수는 모두 동일 → 문제없음

💡 4번 문제의 경우 새로 삽입된 노드의 부모 역시 빨간색이므로 3가지의 방법으로 나누어 처리한다. 부모노드가 조부 노드의 왼쪽 자식일 경우를 기준이며, 오른쪽 자식일 경우 반대로 진행한다.

 

  1. 삼촌노드가 RED 인 경우
  2. 삼촌은 BLACK이며 새로 삽입한 노드가 부모노드의 오른쪽 자식인 경우
  3. 삼촌은 BLACK이며 새로 삽입한 노드가 부모노드의 왼쪽 자식인 경우

 

  1. 삼촌노드가 RED 인 경우

 

  • 새로 삽입된 노드는 12이며, 삼촌(75)이 RED
  1. 부모노드와 삼촌노드를 검은색으로 칠한다
  2. 조부노드를 빨간색으로 칠한다

💡 조부 노드를 RED로 만들었기 때문에 그 위로 규칙 위반이 없는지 루트노드까지 재귀적으로 살펴야 한다

 

 

2. 삼촌은 BLACK이며 새로 삽입한 노드가 부모노드의 오른쪽 자식인 경우

  • 새로 삽입된 노드는 75이며, 삼촌(NULL LEAF)이 BLACK
  • 부모노드(50)를 왼쪽으로 회전시켜서 3번 문제와 같게 만든다

3. 삼촌은 BLACK이며 새로 삽입한 노드가 부모노드의 왼쪽 자식인 경우

  • 새로 삽입된 노드는 25이며, 삼촌(NULL LEAF)이 BLACK
  • 부모노드를 검은색, 조부노드를 빨간색으로 칠한다
  • 조부노드를 오른쪽으로 회전시킨다

 

 

삭제연산

레드-블랙트리에서의 삭제는 기본적으로 이진탐색트리의 방법을 따른 뒤, 색상을 맞춘다

 

1. 형제 노드가 RED일 경우

  • 회전 -> 부모와 형제 모두 색 변경

 

2. 부모, 형제, 모든 조카가 BLACK일 경우

  • 형제 노드만 RED로 변경

 

3. 부모가 RED고, 형제와 모든 조카가 BLACK일 경우

  • 부모와 형제 색 변경

 

4. 본인이 왼쪽에 있고, 가까운 위치의 조카가 RED일 경우

  • 조카가 빨강 → 형제는 검정
  • 가까운 조카 BLACK, 형제 RED로 색 변경 후 회전

 

5. 본인 위치 기준으로 먼 조카가 RED일 경우

  • 흰색은 검정 또는 빨강이 모두 될 수 있다
  • 부모를 BLACK, 형제의 색을 부모의 색으로, 먼 조카의 색을 BLACK으로 순차 변경
  • 형제 기준으로 부모를 회전

6. 만약 끝 노드가 아니라 중간 노드가 삭제될 경우

  • 삭제하고자 하는 노드를 기준으로 오른쪽에 있는 값 중에 가장 작은 값을 후보로 올린다. 여기서 값만 옮기고, 색은 옮기지 않고 그대로 사용

  • 규칙 5번에 의해 부모를 BLACK, 형제를 부모의 색으로, 먼 조카를 BLACK으로 변경 후 회전을 진행한다

 

레드-블랙 트리 코드

  • 삽입
#include <stdio.h>                                                                                                                                      
#include <stdlib.h>
#include "rbtree.h"
#define INFO_NUM 8

typedef struct {
    int data;
    struct rb_node node;
} INFO;

typedef struct {
    int data;
    int color;
} NODE_INFO;

int rb_insert(struct rb_root *root, INFO *info) {
    struct rb_node **p = &root->rb_node;
    struct rb_node *parent = 0;
    INFO *temp;
    while(*p) {
        // Container_of
        parent = *p;
        temp = rb_entry( parent, INFO, node);

        if(info->data < temp->data)
            p = &(*p)->rb_left;
        else if(info->data > temp->data)
            p = &(*p)->rb_right;
        else
            return 0;
    }
    // Insert node
    rb_link_node(&info->node, parent, p);
    // Balance tree
//  rb_insert_color(&info->node, root);

    return 1;
}
int __display(struct rb_node *temp, NODE_INFO (*array)[10], int *row, int *col) {
    INFO *info;
    if(temp == 0)
        return 0;

    ++*row;
    __display(temp->rb_left, array, row, col);
    info = rb_entry(temp, INFO, node);
    array[*row][(*col)].color = temp->rb_color;
    array[*row][(*col)++].data = info->data;
    __display(temp->rb_right, array, row, col);
    --*row;
}

void display(struct rb_root *root) {
    int row = -1;
    int col = 0;
    NODE_INFO a[10][10] = {0,};
    __display(root->rb_node, a, &row, &col);
    
    system("clear");
    for(int i=0; i<10; i++) {
        for(int j=0; j<10; j++) {
            if(a[i][j].data) {
                if(a[i][j].color == RB_RED)
                    printf("<%2d>", a[i][j].data);
                else
                    printf("[%2d]", a[i][j].data);
            }
            else
                printf("%4c", ' ');
        }
        printf("\\n");
    }
    getchar();
}

int main() {
    struct rb_root root = {0};
    int a[] = {1, 2, 3, 4, 5, 6, 7, 8};
    int i;
    INFO info[INFO_NUM];

    display(&root);
    for(i=0; i<INFO_NUM; i++) {
        info[i].data = a[i];
        rb_insert(&root, info+i);
        display(&root);
    }

    return 0;
}
  • 삭제
#include <stdio.h>                                                                                                                                      
#include <stdlib.h>
#include "rbtree.h"
#define INFO_NUM 8

typedef struct {
    int data;
    struct rb_node node;
} INFO;

typedef struct {
    int data;
    int color;
} NODE_INFO;

int rb_insert(struct rb_root *root, INFO *info) {
    struct rb_node **p = &root->rb_node;
    struct rb_node *parent = 0;
    INFO *temp;
    while(*p) {
        // Container_of
        parent = *p;
        temp = rb_entry( parent, INFO, node);

        if(info->data < temp->data)
            p = &(*p)->rb_left;
        else if(info->data > temp->data)
            p = &(*p)->rb_right;
        else
            return 0;
    }
    // Insert node
    rb_link_node(&info->node, parent, p);
    // Balance tree
//  rb_insert_color(&info->node, root);

    return 1;
}
int __display(struct rb_node *temp, NODE_INFO (*array)[10], int *row, int *col) {
    INFO *info;
    if(temp == 0)
        return 0;

    ++*row;
    __display(temp->rb_left, array, row, col);
    info = rb_entry(temp, INFO, node);
    array[*row][(*col)].color = temp->rb_color;
    array[*row][(*col)++].data = info->data;
    __display(temp->rb_right, array, row, col);
    --*row;
}

void display(struct rb_root *root) {
    int row = -1;
    int col = 0;
    NODE_INFO a[10][10] = {0,};
    __display(root->rb_node, a, &row, &col);
    
    system("clear");
    for(int i=0; i<10; i++) {
        for(int j=0; j<10; j++) {
            if(a[i][j].data) {
                if(a[i][j].color == RB_RED)
                    printf("<%2d>", a[i][j].data);
                else
                    printf("[%2d]", a[i][j].data);
            }
            else
                printf("%4c", ' ');
        }
        printf("\\n");
    }
    getchar();
}

int main() {
    struct rb_root root = {0};
    int a[] = {1, 2, 3, 4, 5, 6, 7, 8};
    int i, index;
    INFO info[INFO_NUM];

    for(i=0; i<INFO_NUM; i++) {
        info[i].data = a[i];
        rb_insert(&root, info+i);
    }

    display(&root);
    while(1) {
        printf("Delete node: ");
        scanf("%d", &index);
        rb_erase(&info[index-1].node, &root);
        display(&root);
    }

    return 0;
}

'알고리즘 공부 > Binary Tree' 카테고리의 다른 글

B-트리  (3) 2022.01.04
트리의 활용  (0) 2022.01.04
AVL 트리  (0) 2022.01.03
트리, 이진 트리  (0) 2022.01.03