Krafton Jungle/2. Keywords

[WEEK06] 레드 블랙 트리 (Red-Black Tree)

munsik22 2025. 4. 17. 14:30

📚 강의 영상

 


Red-Black Tree란?

  • 레드-블랙 트리Red-Black tree는 자가 균형 이진 탐색 트리self-balancing binary search tree의 한 종류로서, 대표적으로는 연관 배열 등을 구현하는 데 쓰이는 자료구조다.
  • 구현하기에는 아주 복잡한 자료구조지만, 실제 사용에서 효율적이고, 최악의 경우에도 상당히 우수한 실행 시간을 보인다.
    • 트리에 n개의 원소가 있을 때 O(log n)의 시간복잡도로 삽입, 삭제, 검색을 할 수 있다.

Red-Black Tree의 특징

  1. 모든 노드는 RED 또는 BLACK으로 구성된다.
  2. 루트 노드는 BLACK
  3. 모든 NIL(leaf) 노드는 BLACK
  4. RED의  자녀들은 BLACK → RED는 연속적으로 존재할 수 없다.
  5. 임의의 노드에서 자손 NIL 까지 BLACK 개수는 동일하다. (자기 자신은 카운트에서 제외)

NIL 노드란?

  • 존재하지 않음을 의미하는 노드
  • 자녀가 없을 때 자녀를 NIL 노드로 표기
  • 값이 있는 노드와 동등하게 취급됨
  • RB 트리에서 리프 노드는 NIL 노드

노드 X의 BLACK height

  • 노드 X에서 임의의 자손 NIL 노드까지 내려가는 경로에서의 BLACK 수 (자기 자신은 카운트에서 제외)
  • 5번 속성을 만족시켜야 성립하는 개념

  • 위 RB 트리에서, 20의 black height = 2
  • 위 RB 트리에서, 50의 black height = 2

색을 바꾸면서도 5번 속성을 유지하기

RB 트리가 5번 속성을 만족하고 있고, 두 자녀가 같은 색을 가질 때, 부모와 두 자녀의 색을 바꿔도 5번 속성은 여전히 만족한다.

각각의 경로에 대해서 BLACK 수는 동일하다

💡 수평으로 배치된 두개의 RED가 있다면, 색상을 BLACK으로 변경하고 부모를 RED로 바꿀수 있다!

RB 트리는 어떻게 균형을 잡을까?

삽입/삭제 시 주로 2, 4, 5번 규칙을 위반하며, 이들을 해결하려고 구조를 바꾸다 보면 자연스럽게 트리의 균형이 잡히게 된다.


RB 트리 삽입

삽입 Overview

  1. 삽입전 RB트리 속성을 만족한 상태
  2. 삽입 방식은 일반적인 BST와 동일 (삽입노드의 색깔은 항상 RED)
  3. 삽입후 RB트리 속성 위반 여부 확인
  4. RB트리 속성을 위반했다면 재조정 (insert-fixup)
  5. RB트리 속성을 다시 만족
💡 여기서 삽입 노드가 무조건 RED인 이유는 삽입 후에도 5번 속성을 만족시키기 위해서다.

삽입에 대해 다루기 전에, 우선 회전에 대해 알아보자.

Left Rotatation

left-rotation은 그림에서 두 노드(x와 y)를 위 그림에서 아래 그림처럼 이동시키며, 이 과정에서 BST의 속성을 유지한다.

void left_rotate(rbtree *t, node_t *x) {
  if (t != NULL) {
    node_t *y = x->right;
    x->right = y->left;
    if (y->left != t->nil) y->left->parent = x;
    y->parent = x->parent;
    if (x->parent == t->nil) t->root = y;
    else if (x == x->parent->left) x->parent->left = y;
    else x->parent->right = y;
    y->left = x;
    x->parent = y;
  }
}

Right Rotation

right-rotate는 left-rotate와 대칭적이다.

void right_rotate(rbtree *t, node_t *y) {
  if (t != NULL) {
    node_t *x = y->left;
    y->left = x->right;
    if (x->right != t->nil) x->right->parent = y;
    x->parent = y->parent;
    if (y->parent == t->nil) t->root = x;
    else if (y == y->parent->right) y->parent->right = x;
    else y->parent->left = x;
    x->right = y;
    y->parent = x;
  }
}

🔹 insert 50

  • 삽입 노드의 색깔는 RED (50)
  • 노드를 삽입할 때 두 NIL 노드의 색은 BLACK으로 고정 (3번 속성 만족)
  • 루트 노드가 BLACK이라는 2번 속성 위반 → 루트 노드 색을 BLACK으로 변경 (50)

🔹 insert 20

  • 삽입 노드의 색깔은 RED (20)

🔹 insert 10

  • 삽입 노드의 색깔은 RED (10)
  • 4번 속성 위반! (RED 노드의 자식은 무조건 BLACK이어야 함!)
  • RED가 한 쪽으로 몰려 있으니 RED 하나를 반대편으로 옮겨주자!
  • 구조를 바꾼 이후에도 BST의 특징 또한 유지되어야 한다는 것을 잊지 말자.

  • 구조를 바꾸면서도 BST의 특징을 유지시키는 방법은 회전이다.
  • 4번 속성 위반을 해결하기 위해 RED 하나를 넘겨야 하는데, BST의 특징 또한 유지하면서 넘기려면 회전을 사용해야 한다.
    → 회전을 어떻게 사용할 지가 관건!
    • 2050의 색을 바꿔준다 → 20, 50
    • 50을 기준으로 오른쪽으로 회전한다
[Case 3] RED 삽입 후 4번 속성을 위반했을 때 (RED의 자식이 RED인 경우):
🔹 삽입된 RED 노드가 부모의 왼쪽 자녀 and 부모도 RED고 할아버지의 왼쪽 자녀 and 삼촌(부모의 형제)가 BLACK이면
→ 부모와 할아버지의 색을 바꾼 후 할아버지 기준으로 오른쪽으로 회전한다
🔹 삽입된 RED 노드가 부모의 오른쪽 자녀 and 부모도 RED고 할아버지의 오른쪽 자녀 and 삼촌(부모의 형제)가 BLACK이면
→ 부모와 할아버지의 색을 바꾼 후 할아버지 기준으로 왼쪽으로 회전한다

🔹 insert 40

  • 삽입 노드의 색깔은 RED (40)
  • 4번 속성 위반!
  • 위의 case 3괴 다른 점은, 삽입된 노드를 기준으로 할아버지까지의 경로가 꺾였다는 점
  • 꺾인 부분을 펴줘서 위 case 3과  같은 형태로 만들면 위 case 3과 같은 방식으로 해결 가능!

  • 20을 기준으로 왼쪽으로 회전

  • 4050의 색을 바꿔준다 → 40, 50
  • 50 기준으로 오른쪽으로 회전한다
[Case 2] RED 삽입 후 4번 속성을 위반했을 때:
🔹 삽입된 RED 노드가 부모의 오른쪽 자녀 and 부모도 RED고 할아버지의 왼쪽 자녀 and 삼촌은 BLACK이라면
→ 부모를 기준으로 왼쪽으로 회전한 뒤 Case 3의 방식으로 해결
🔹 삽입된 RED 노드가 부모의 왼쪽 자녀 and 부모도 RED고 할아버지의 오른쪽 자녀 and 삼촌은 BLACK이라면
→ 부모를 기준으로 오른쪽으로 회전한 뒤 Case 3의 방식으로 해결

🔹 insert 30

  • 삽입 노드의 색깔은 RED (30)
  • 4번 속성 위반!
  • 앞의 두 case와는 다르게, RED가 한쪽으로 몰려 있지 않아서 옮길 수가 없다😵

  • 4번 속성을 만족시키면서 5번 속성을 유지하려면, 1050BLACK으로 바꾸고 20RED로 바꾸면 된다
  • But, 20이 루트 노드라서 2번 속성 위반! → 20BLACK으로 바꿔준다 (20)

[Case 1] RED 삽입 후 4번 속성을 위반했을 때:
삽입된 RED 노드의 부모도 RED and 삼촌도 RED라면
→ 부모와 삼촌을 BLACK으로 바꾸고 할아버지를 RED로 바꾼 뒤 할아버지에서 다시 확인을 시작한다

📝 슈도코드 작성하기

while (부모가 RED) {

    // CASE 1. 
    if 부모/삼촌 == RED이면, 
        부모/삼촌 모두 BLACK으로 바꾸고, 할아버지를 RED로 변경
        할아버지에서 다시 시작
        
    // CASE 2/3. 삼촌 == BLACK
    else {
        // CASE 2. 
        if 할아버지/부모/자신 꺾인상테
            회전해서 부모/자신을 펴준다. CASE 3 상태가 됨
        
        // CASE 3. 할아버지/부모/자신 펴진상테
        부모/할아버지 서로 색바꾸기
        할아버지 기준 회전
    }
}

// 종료전
루트를 BLACK으로 변경

RB 트리 삭제

삭제 Overview

  1. 삭제 전 RB 트리 속성을 만족한 상태
  2. 삭제 방식은 일반적인 BST와 동일
  3. 삭제 후 RB 트리 속성 위반 여부 확인
  4. RB 트리 속성을 위반했다면 재조정 (delete-fixup)
  5. RB 트리 속성을 다시 만족

속성 위반 여부 확인

  • RB 트리에서 노드를 삭제할 때 어떤 색이 삭제되는지가 속성 위반 여부를 확인할 때 매우 중요하다!

1️⃣ 삭제하려는 노드의 자녀가 없거나 하나라면, 삭제되는 색 = 삭제되는 노드의 색

(여기서는 유효한 값을 가지는 자녀를 의미함. 즉, NIL 노드는 포함하지 않음)

  • 25 삭제 → RED 삭제
  • 80 삭제 → BLACK 삭제
  • 40 삭제 → BLACK 삭제

2️⃣ 삭제하려는 노드의 자녀가 둘이라면, 삭제되는 색 = 삭제되는 노드의 후임자successor의 색

  • 20 삭제 → 후임자 = 25RED 삭제
  • 35 삭제 → 후임자 = 37 RED 삭제
  • 50 삭제 → 후임자 = 80 BLACK 삭제
  • 삭제되는 색이 RED라면, 어떠한 속성도 위반하지 않는다.
  • 삭제되는 색이 BLACK이라면, 2, 4, 5번 속성을 위반할 수 있다. → 재조정 필요!
    • 40 삭제 → BLACK 삭제 → 4, 5번 속성 위반!

2번 속성 위반 해결하기

→ 루트 노드를 BLACK으로 바꾸면 된다

35 삭제 전

  • 35 삭제 → BLACK 삭제 → 50이 루트 노드가 되고 2번 속성 위반 → 50BLACK으로 바꿔 해결 (50)

35 삭제 후

삭제 후 5번 속성 위반 시 EXTRA BLACK 부여

삭제되는 색이 BLACK일 때, 특수한 상황을 제외하면 5번 속성을 항상 위반하게 된다. 5번 속성을 다시 만족시키기 위해 삭제된 색의 위치를 대체한 노드에 EXTRA BLACK을 부여한다.

 

경로에서 BLACK 수를 카운트할 때, EXTRA BLACK은 하나의 BLACK으로 카운트된다.


🔹 delete 10

 

10 삭제 전

  • 10 삭제 → BLACK 삭제 → 5번 속성 위반!
  • EXTRA BLACK을 부여해서 5번 속성을 다시 만족시켜야 한다

10 삭제 후

  • 삭제한 색의 위치를 대체한 노드에 EXTRA BLACK을 부여해야 한다.
  • 삭제된 색은 10BLACK이므로 10의 위치를 대체한 노드인 NIL 노드에 EXTRA BLACK을 부여
  • 20의 black height는 NILBLACKEXTRA BLACK까지 2가 된다. 30을 통해서도 30, NIL로 2다. 즉, 5번 속성을 만족하게 된다.

NIL과 같이 EXTRA BLACK이 부여된 BLACK 노드를 DOUBLY BLACK 노드라고 부른다.


🔹 delete 30

30 삭제 전

  • 30 삭제 → 30은 자녀가 하나라서 삭제되는 색은 BLACK

30 삭제 후

  • 5번 속성 위반이므로 EXTRA BLACK 부여
  • 삭제된 색은 30 BLACK이었으므로, 30을 대체한 노드인 25EXTRA BLACK 부여

25에 EXTRA BLACK 부여 이후

  • 25EXTRA BLACK 부여 후 다시 5번 속성 만족

25와 같이 EXTRA BLACK이 부여된 RED 노드를 RED-AND-BLACK 노드라고 부른다.


🔹 delete 50

50 삭제 전

  • 50은 자녀가 둘이라서 삭제되는 색은 50의 후임자인 80BLACK → 5번 속성 위반!

 

50 삭제 후

  • 80의 위치를 대체한 NIL 노드에 EXTRA BLACK 부여 → 5번 속성 다시 만족
삭제되는 색이 BLACK이고, 5번 속성 위반일 때:
EXTRA BLACK을 부여받은 노드는 DOUBLY BLACK이 되거나 RED-AND-BLACK이 된다.

🔸 RED-AND-BLACK 해결하기

→  RED-AND-BLACK을 BLACK으로 바꾸면 해결

  • 30을 삭제하면 4번, 5번 속성 위반

  • 30을 대체한 25EXTRA BLACK 부여

  • 25RED-AND-BLACK이 되었으니 그냥 BLACK으로 바꿔주면 끝!

🔸 DOUBLY BLACK 해결하기

결국 어떻게 EXTRA BLACK을 없앨것인지가 관건

네 가지의 case로 분류할 때의 기준은 DOUBLY BLACK의 형제의 색과, 그 형제들의 자녀의 색

4️⃣ DOUBLY BLACK의 오른쪽 형제가 BLACK and 그 형제의 오른쪽 자녀가 RED일 때:

REDDOUBLY BLACK 위로 옮기고, 옮긴 REDEXTRA BLACK을 전달해서 RED-AND-BLACK으로 만들기

RED-AND-BLACK을 BLACK으로 바꿔서 해결

바꾸기 전

오른쪽 형제는 부모의 색으로, 오른쪽 형제의 오른쪽 자녀는 BLACK으로, 부모는 BLACK으로 바꾼 후에

바꾼 후

부모를 기준으로 왼쪽으로 회전하면 해결

회전 이후

Case 4

[Case 4]
🔹 DOUBLY BLACK오른쪽 형제가 BLACK and 그 형제의 오른쪽 자녀가 RED일 때:
오른쪽 형제는 부모의 색으로, 오른쪽 형제의 오른쪽 자녀는 BLACK으로, 부모는 BLACK으로 바꾼 후에 부모를 기준으로 왼쪽으로 회전하면 해결
🔹 DOUBLY BLACK 왼 형제가 BLACK and 그 형제의 왼쪽 자녀가 RED일 때:
 왼쪽 형제는 부모의 색으로, 왼쪽 형제의 왼쪽 자녀는 BLACK으로, 부모는 BLACK으로 바꾼 후에 부모를 기준으로 오른으로 회전하면 해결

3️⃣ DOUBLY BLACK의 오른쪽 형제가 BLACK and 그 형제의 왼쪽 자녀가 RED and 그 형제의 오른쪽 자녀는 BLACK일 때:

DOUBLY BLACK의 형제의 오른쪽 자녀가 RED가 되게 만들어서 이후에는 Case 4를 적용해 해결

바꾸기 전

→ E 위치에 RED가 오도록 만들기 위해 C와 D의 색을 바꾼 후에

바꾼 후

D를 기준으로 오른쪽으로 회전하면 된다

회전 이후

이제 Case 4를 적용해서 해결한다.

C는 B의 색으로, B와 D는 BLACK으로 바꾼 후 B를 기준으로 왼쪽으로 회전하면 해결

Case 4 적용 후

Case 3

[Case 3]
🔹 DOUBLY BLACK 오른쪽 형제가 BLACK and 그 형제의  자녀가 RED and 오른쪽 자녀가 BLACK일 때:
DOUBLY BLACK 형제의 오른쪽 자녀를 RED가 되게 만들어서 이후에는 Case 4를 적용하여 해결
🔹 DOUBLY BLACK 왼쪽 형제가 BLACK and 그 형제의 오른 자녀가 RED and 왼쪽 자녀가 BLACK일 때:
 DOUBLY BLACK 형제의 왼쪽 자녀를 RED가 되게 만들어서 이후에는 Case 4를 적용하여 해결

2️⃣ DOUBLY BLACK 형제가 BLACK and 그 형제의 두 자녀가 모두 BLACK일 때:

 DOUBLY BLACK 그 형제의 BLACK을 모두 모아서 부모에게 전달해서 부모가 EXTRA BLACK을 해결하도록 한다

BLACK을 모아 부모에게 전달하기 전

→ A와 A의 형제인 D의 BLACK을 모아서 부모에게 전달하게 되면

→ A와 D에서 BLACK을 모았기 때문에 A는 여전히 BLACK이고 D는 RED가 된다

모은 BLACK을 부모에게 전달한 후

BLACK을 전달받은 B는 RED-AND-BLACK 또는 DOUBLY BLACK이 된다 (5번 속성은 여전히 만족됨)

B가 RED-AND-BLACK이 됐다면 BLACK으로 바꿔주면 상황은 종료되지만

 B가 DOUBLY BLACK이 됐다면: B가 루트 노드라면 BLACK으로 바꿔서 해결, 아니라면 Case 1, 2, 3, 4 중 하나로 해결

Case 2

[Case 2] DOUBLY BLACK의 형제가 모두 BLACK and 그 형제의 두 자녀가 모두 BLACK일 때
DOUBLY BLACK그 형제의 BLACK을 모두 모아서 부모에게 전달해서 부모가 EXTRA BLACK을 해결하도록 한다

1️⃣ DOUBLY BLACK의 형제가 RED일 때:

 DOUBLY BLACK 형제를 BLACK으로 만든 후 Case 2, 3, 4 중에 하나로 해결

→ B를 기준으로 왼쪽으로 회전하면 DOUBLY BLACK A의 형제는 C가 되며, 즉 BLACK이 된다

회전 후에도 5번 속성을 만족하려면 왼쪽으로 회전하기 전에 B와 D의 색을 바꿔준다

B와 D의 색을 바꾼 모습
회전 후

DOUBLY BLACK 형제가 BLACK이 되었으므로 Case 2, 3, 4 중에 해결책을 찾으면 된다.

Case 1

[Case 1]
🔹 DOUBLY BLACK 오른쪽 형제가 RED일 때:
부모와 형제의 색을 바꾸고 부모를 기준으로 왼쪽으로 회전한 뒤 DOUBLY BLACK을 기준으로 Case 2, 3, 4 중 하나로 해결
🔹 DOUBLY BLACK 왼쪽 형제가 RED일 때:
 부모와 형제의 색을 바꾸고 부모를 기준으로 오른쪽으로 회전한 뒤 DOUBLY BLACK을 기준으로 Case 2, 3, 4 중 하나로 해결


BLACK 삭제 시 5번 속성 위반 해결 방법 요약

  • 삭제되는 색이 BLACK일 때 삭제되는 색이 있던 위치를 대체한 노드에 EXTRA BLACK을 부여한다
  • 대체한 노드가 RED-AND-BLACK이 되었다면 BLACK으로 바꿔주면 끝
  • 대체한 노드가 DOUBLY BLACK이 되었다면 Case 1, 2, 3, 4 중에 하나로 해결

📝 슈도코드

while (타겟이 루트아님 && 타겟 == BLACK) {

    // CASE 1.
    if 형제 == RED
        형제/부모 서로 색바꾸기, 부모기준 회전 (타겟이 내려가도록)

    // CASE 2.
    if 형제 == BLACK, 형제의 자식 둘다 블랙
        형제/자신의 블랙을 부모로 올리기 -> 형제를 RED로 변경, 부모에서 다시 fixup

    else {
        // CASE 3.
        if 형제 == BLACK, (형제의 꺾인 자식) == RED
            자식 RED와 형제 서로 색바꾸기, 펴지게 회전 (RED가 바깥쪽에 오게)
            CASE 4가 됨

        // CASE 4. 형제 == BLACK, (형제의 펴진 자식) == RED
        부모/형제 서로 색바꾸기
        형제의(펴진 자식) = BLACK
        부모기준 회전 (타겟이 내려가도록)
        타겟을 루트로 설정 -> while 종료
    }
}

// 종료전
타겟을 BLACK으로 변경

RB Tree의 시간 복잡도

  average-case worst-case
insert O(log n) O(log n)
delete O(log n) O(log n)
search O(log n) O(log n)

RB Tree vs AVL Tree

  RB Tree AVL Tree
BST O O
시간 복잡도 O(log n) O(log n)
삽입/삭제 성능 AVL 트리보다 빠르다 RB 트리보다 느리다
검색 성능 AVL 트리보다 느리다 RB 트리보다 빠르다
균형 잡는 방식 RB 트리 속성을 만족시키도록 balance factor ∈ {-1, 0, 1}이 되도록
응용 사례 linux kernel, Java Treemap 등... dictionary, 삽입/삭제가 거의 없도 검색이 대부분인 상황에서 사용

[자료 출처]

 

Build the Forest in Python Series: Red-Black Tree - Ilha Formosa 1544

[Updated: December 31, 2021] From the Binary Search Tree: Analysis, we know the tree height is the critical factor of binary search tree’s performance. This article and the following article will implement one variant of the binary search tree that will

www.formosa1544.com

 

'Krafton Jungle > 2. Keywords' 카테고리의 다른 글

[WEEK06] AVL 트리  (0) 2025.04.21
[WEEK06] 메모리 누수  (0) 2025.04.18
[WEEK06] 균형 탐색 트리  (0) 2025.04.16
[WEEK05] KMP법, 보이어-무어법  (0) 2025.04.14
[WEEK05] B-Tree  (0) 2025.04.14