user: 레드-블랙 트리의 동작을 알려주세요.

gpt: 레드-블랙 트리의 읽기 전용 동작은 이진 탐색 트리의 읽기 전용 동작의 구현을 변경하지 않아도 되는데, 이는 레드-블랙 트리가 이진 탐색 트리의 특수한 한 형태이기 때문이다. 그러나 삽입과 삭제의 경우 이진 탐색 트리의 구현에 따른 동작만으로는 레드-블랙 트리의 특성을 만족하지 못한다. 다시 레드-블랙 트리의 특성을 만족하게 만들기 위해서는 O 또는 amortized O번의 색 변환과 최대 3회의 트리 회전이 필요하다. 삽입과 삭제는 복잡한 동작이지만, 그 복잡도는 여전히 O이다.

## 삽입
레드-블랙 트리의 삽입은 단순 이진 탐색 트리에서 하는 것과 같이 노드를 삽입하고, 색을 붉은색으로 정하는 것으로 시작한다. 그 다음 단계는, 그 주위 노드의 색에 따라 달라진다. 여기서 '삼촌 노드'를 도입할텐데, 이는 같은 높이에 있는 옆 노드의 부모 노드를 뜻한다. 여기서 레드-블랙 트리의 특성이 추가된다 :

삽입과 삭제 동작은 C언어로 만든 예제를 이용하여 자세히 설명한다. 삼촌 노드와 할아버지 노드는 다음과 같은 함수에 의해 나타낼 수 있다:

struct node *grandparent
 if  && )
 return n->parent->parent;
 else
 return NULL;

struct node *uncle
 struct node *g = grandparent;
 if 
 return NULL; // No grandparent means no uncle
 if 
 return g->right;
 else
 return g->left;

### 첫 번째 경우
**N** 이라고 하는 새로운 노드가 트리의 시작에 위치한다. 이 경우, 레드-블랙 트리의 첫 번째 속성을 만족하기 위해서, **N**을 검은색으로 표시한다. 이 경우, 시작점으로부터 뻗어나가는 모든 경로에 검은색 노드를 하나 추가한 셈이 되기 때문에 레드-블랙 트리의 다섯 번째 속성은 여전히 유효하다.

void insert_case1
 if 
 n->color = BLACK;
 else
 insert_case2;

### 두 번째 경우
새로운 노드의 부모 노드 **P**가 검은색이라면, 레드-블랙 트리의 네 번째 속성은 유효하다. 그러므로 두 번째 경우에도 이 트리는 적절한 레드-블랙 트리이다. 레드-블랙 트리의 다섯 번째 속성도 별 문제가 없는데, 이는 새로운 노드 **N**은 두개의 검은색 노드를 leaf node로 가지고 있기 때문이다. 비록 **N**이 붉은색 노드라고 해도 **N**이 들어간 자리에 원래 위치했던 노드의 색이 검은색이었으므로, **N**의 자식 노드에게서 시작되는 경로들은 모두 같은 수의 검은색 노드를 가지게 되고, 결과적으로 다섯 번째 속성은 유지되게 된다.

void insert_case2
 if 
 return; /* Tree is still valid */
 else
 insert_case3;

또한 **N**은 삼촌 노드가 있다고 가정할 수 있는데, 위의 네 번째, 다섯 번째 노드에서는 leaf node에 해당한다.

### 세 번째 경우
만약 부모 노드 **P**와 삼촌 노드 **U**가 모두 붉은색 노드라면, 레드-블랙 트리의 다섯 번째 속성을 유지하기 위해서, **P**와 **U**를 모두 검은색으로 바꾸고 할아버지 노드 **G**를 붉은색으로 바꾼다. 이렇게 함으로써 붉은색 노드인 **N**은 검은색 부모 노드를 가지게 된다. 그런데 이번에는 할아버지 노드 **G**가 레드 블랙 트리의 두 번째 속성이나 네 번째 속성을 만족하지 않을 수 있다. 이를 해결하기 위해서 **G**에 대해 지금까지 설명한 첫 번째 경우부터 세 번째 경우까지를 재귀적으로 적용한다. 이 작업은 삽입 과정 중에 발생하는 유일한 재귀 호출이며, 회전 작업을 하기 전에 적용해야 한다는 것에 주의한다.

void insert_case3
 struct node *u = uncle, *g;

 if  && ) {
 n->parent->color = BLACK;
 u->color = BLACK;
 g = grandparent;
 g->color = RED;
 insert_case1;
 insert_case4;

### 네 번째 경우
부모 노드 **P**가 붉은색인데, 삼촌 노드 **U**는 검은색이다; 또한 새로운 노드 **N**은 **P**의 오른쪽 자식 노드이며, **P**는 할아버지 노드 **G**의 왼쪽 자식 노드이다. 이 경우, **N**과 **P**의 역할을 변경하기 위해서 왼쪽 회전을 한다; 그 후, 부모 노드였던 **P**를 다섯 번째 경우에서 처리하게 되는데, 이는 레드-블랙 트리의 네 번째 속성을 아직 만족하지 않기 때문이다. 회전 작업은 몇몇 경로들 이 이전에는 지나지 않았던 노드를 지나게 하는데, 그럼에도 양쪽 노드가 모두 붉은색이므로, 레드-블랙 트리의 다섯 번째 속성을 위반하지 않는다.

void insert_case4
 struct node *g = grandparent;

 if  && ) {
 rotate_left;
 n = n->left;
 rotate_right;
 n = n->right;
 insert_case5;

static void rotate_left
 struct node *c = n->right;
 struct node *p = n->parent;

 if 
 c->left->parent = n;

 n->right = c->left;
 n->parent = c;
 c->left = n;
 c->parent = p;

 if  {
 if 
 p->left = c;
 else
 p->right = c;

static void rotate_right
 struct node *c = n->left;
 struct node *p = n->parent;

 if 
 c->right->parent = n;

 n->left = c->right;
 n->parent = c;
 c->right = n;
 c->parent = p;

 if  {
 if 
 p->right = c;
 else
 p->left = c;

### 다섯 번째 경우
부모 노드 **P**는 붉은색이지만 삼촌 노드 **U**는 검은색이고, 새로운 노드 **N**이 **P**의 왼쪽 자식 노드이고, **P**가 할아버지 노드 **G**의 왼쪽 자식 노드인 상황에서는 **G**에 대해 오른쪽 회전을 한다. 회전의 결과 이전의 부모 노드였던 **P**는 새로운 노드 **N**과 할아버지 노드 **G**를 자식 노드로 갖는다. **G**가 이전에 검은색이었고, **P**는 붉은색일 수밖에 없기 때문에, **P**와 **G**의 색을 반대로 바꾸면 레드-블랙 트리의 네 번째 속성을 만족한다. 레드-블랙 트리의 다섯 번째 속성은 계속 유지되는데, 이는 이전에 **P**를 포함하는 경로는 모두 **G**를 지나게 되고, 바뀐 후 **G**를 포함하는 경로는 모두 **P**를 지나기 때문이다. 바뀌기 전에는 **G**가, 바뀐 후에는 **P**가 **P**, **G**, **N**중 유일한 검은색 노드이다.

void insert_case5
 struct node *g = grandparent;

 n->parent->color = BLACK;
 g->color = RED;
 if 
 rotate_right;
 else
 rotate_left;

삽입 동작은 치환 작업인데, 이는 모든 호출이 tail recursion이기 때문이다.

## 삭제
이진검색트리에서 두 개의 non-leaf 자식 노드를 가진 노드 X를 삭제할 때, X의 왼쪽 서브트리에서 최댓값 또는 오른쪽 서브트리에서 최솟값을 갖는 노드 Y를 찾고, X에 Y의 값을 복사한다. 그리고 Y를 지운다.

이때 Y가 가진 non-leaf 자식 노드는 1개 이하이다. Y의 non-leaf 자식 노드가 2개라면, Y의 값은 최댓값/최솟값이 아닐 것이기 때문이다.  따라서 삭제는 최대 한 개의 non-leaf 자식을 갖고 있는 노드를 삭제하는 문제로 치환할 수 있다. 그러므로, 본 문단에서는 한 개 이하의 non-leaf 자식 노드를 가진 노드를 삭제하는 방법만 다룬다.

**M**은 삭제하고자 하는 노드이고, **C**는 **M**의 선택된 자식 노드이다. 만약 **M**이 non-leaf 자식을 가질 경우, 해당 자식 노드가 **C**가 되고, 그렇지 않은 경우 두 leaf 노드 중 아무 노드가 **C**가 된다.

만약 **M**이 붉은 노드라면, **C**를 **M**대신 치환하면 된다. 이 상황은 **M**이 두 개의 leaf 자식을 갖고 있을 때에만 발생하는데, 왜냐하면 만약 **M**이 non-leaf 자식 노드와 leaf 자식 노드를 모두 가지는 경우 양쪽의 검은 노드 개수가 달라지기 때문이다. 삭제된 노드를 지나는 모든 path는 단순히 붉은 노드 하나만 없어지게 되고, 삭제된 노드의 부모와 자식 모두 검은 노드이므로, 특성 3과 특성 4는 여전히 유효하다.

또 다른 상황은 **M**이 검은 노드이고 **C**가 붉은 노드일 때이다. 검은 노드를 삭제해버릴 경우 특성 4에 위반될 수 있으므로, **C**를 검은 노드로 바꿔주면 된다.

복잡한 상황은 **M**과 **C**가 모두 검은색 노드일 때이다. 이 상황은 두 개의 leaf 자식을 갖고 있는 검은 노드를 지우는 상황에서만 발생하는데, 왜냐하면 **M**이 non-leaf 자식과 leaf 자식을 모두 가지는 경우, 양 쪽의 검은 노드 개수가 달라지기 때문이다. 이러한 경우 **M**을 자식 노드 **C**로 치환한다. 이후 설명부터는 **C**를 **N**, **C**의 형제 노드를 **S**, **N**의 새로운 부모를 **P**, **S**의 왼쪽/오른쪽 자식을 각각 **S**이라고 하겠다.

다음의 함수를 사용하여 형제 노드를 찾을 수 있다.

struct node *sibling
 if 
 return n->parent->right;
 else
 return n->parent->left;

우리는 다음의 코드를 통해 위에서 소개한 단계를 밟아나갈 수 있다. 여기서 
void replace_node
 /*
 child->parent = n->parent;
 if 
 n->parent->left = child;
 else if 
 n->parent->right = child;

void delete_one_child
 /*
 struct node *child = is_leaf ? n->left: n->right;

 replace_node;
 if  {
 if 
 child->color = BLACK;
 else
 delete_case1;
 free;

만약 **N**과 **N**의 원래 부모가 둘 다 검정이었다면, 부모를 삭제하는 것은 **N**을 지나는 path가 검은 노드 한 개를 덜 갖게 되므로 해서는 안된다. 이는 특성 5를 위반하기 때문에, 트리는 재균형을 맞춰주어야 한다. 고려해보아야 할 몇몇 상황들이 있다:

**Case 1:** **N** 이 새로운 루트가 될때. 이 경우에는 더 할게 없다. 우리는 모든 경로에서 하나의 검은 노드를 삭제했고, 새로운 루트 노드는 검은색이므로 모든 특성이 보존된다.

void delete_case1
 if 
 delete_case2;

**Case 2:** **S** 가 빨강일 경우. **P**의 자식인 **S**가 빨강색이므로 **P**가 검은색임이 명확하다.이 경우 **P**와 **S**의 색을 바꾸고, **P**에서 왼쪽으로  하면, **S**가 **N**의 할아버지 노드가 된다. 모든 경로에서의 검은 노드의 수가 같지 않으므로 아직 끝나지 않았다. 이제 **N**이 검은색 형제노드와 붉은색 부모 노드 갖고 있으므로, case 4, 5, 6을 진행할 수 있다. 
이후의 case들에서 우리는 **N**의 새로운 형제노드를 **S**로 표기하겠다.

void delete_case2
 struct node *s = sibling;

 if  {
 n->parent->color = RED;
 s->color = BLACK;
 if 
 rotate_left;
 else
 rotate_right;
 delete_case3;

**Case 3:** **P**, **S**, 그리고 **S**의 자식들이 검은색인 경우. 이 경우, 우리는 간단히 **S**를 빨강으로 칠하면 된다. 그 결과, **S**를 지나는 모든 경로들은 하나의 검은노드를 적게 갖고 있게 된다. 하지만, **N**의 원래 부모노드를 삭제하는 과정에서 **N**을 지나는 모든 경로들은 하나의 검은 노드를 적게 갖게 되므로, 양쪽은 같은 수의 검은 노드를 갖게 된다. 그러나, **P**를 지나는 모든 경로들은 **P**를 지나지 않는 모든 경로에 대해 검은 노드를 한 개 적게 지니게 되므로, 특성 5를 위반하게 된다. 이를 바로잡기 위해서, 우리는 **P**에다 case 1부터 시작하는 rebalancing 과정을 수행해야 한다.

void delete_case3
 struct node *s = sibling;

 if  &&
  &&
  &&
 ) {
 s->color = RED;
 delete_case1;
 delete_case4;

**Case 4:** **S**와 **S**의 자식들은 검은색이지만, P는 빨강인 경우. 이 경우, 우리는 단순히 **S**와 **P**의 색을 바꾸면 된다. 이는 **S**를 지나는 경로의 검은 노드 개수에 영향을 주지 않지만, **N**을 지나는 경로에 대해서는 검은 노드의 개수를 1개 증가시킨다. 이는 원래 삭제된 검은 노드의 개수를 보충해준다.

void delete_case4
 struct node *s = sibling;

 if  &&
  &&
  &&
 ) {
 s->color = RED;
 n->parent->color = BLACK;
 delete_case5;

**Case 5:** **S**가 검정, **S**의 왼쪽 자식이 빨강, **S**의 오른쪽 자식이 검정이며, **N**이 부모의 왼쪽 자식인 경우. 이 경우 우리는 **S**를 오른쪽으로 회전시켜서 **S**의 왼쪽 자식이 **S**의 부모노드이자 **N**의 새로운 형제 노드가 되도록 만든다. 그리고 나서 우리는 **S**의 색을 부모 노드의 색깔과 바꾼다. 모든 경로는 여전히 같은 수의 검은 노드수를 가지나, 이제 **N**이 오른쪽에 붉은 노드를 자식으로 둔 검은색 형제노드를 갖게 되었으므로, 6번째 case로 진행하면 된다. **N**이나 **N**의 부모노드는 이 변형에 아무런 영향을 받지 않는다.

void delete_case5
 struct node *s = sibling;

 if  {
 /* 이 문은 자명하다,
 case 2로 인해서. */
 /* 다음의 문은 빨강을 **N**의 부모노드의 오른쪽 자식의 오른쪽 자식으로 두기 위함이다.
 혹은 **N**의 부모노드의 왼쪽 자식의 왼쪽 자식으로 두기 위함. case 6에 넘기기 위해 */
 if  &&
  &&
 ) { /* this last test is trivial too due to cases 2-4. */
 s->color = RED;
 s->left->color = BLACK;
 rotate_right;
  &&
 ) {/* this last test is trivial too due to cases 2-4. */
 s->color = RED;
 s->right->color = BLACK;
 rotate_left;
 delete_case6;

**Case 6:** **S**가 검은색, **S**의 오른쪽 자식이 빨강이며, **N** 이 **P**의 왼쪽 자식인 경우. 이 경우 우리는 **P**를 왼쪽으로 회전해서, **S**가 **P**와 **S**의 오른쪽 자식노드의 부모 노드가 되게 하한다. 그리고나서, 우리는 **P**와 **S**의 색을 바꾸고, **S**의 오른쪽 자식노드를 검은색으로 만든다. 이 subtree는 루트노드가 회전하기 전과 마찬가지로 여전히 같은 색을 지니므로 특성 4와 특성 5에 위배되지 않는다. 그러나, **N**은 이제 하나의 추가적인 검은 조상 노드를 가지게 된다:**P**가 검은색으로 바뀌든가, 아니면 **P**가 검은색이었고 **S**가 검은색 할아버지 노드로 추가되었든지. 그러므로, **N**을 지나는 경로는 하나의 추가적인 검은 노드를 갖게 된다.

반면, **N**을 통과하지 않으면, 두 가지의 경우가 존재한다:

어떤 경우든, 검은 노드의 개수는 변화하지 않는다. 그러므로, 우리는 특성 4와 특성 5를 복원할 수 있다. 그림에서 하얀색 노드는 검정이나 빨강 모두가 될 수 있으나, 변형 전과 후에 같은 색을 가리키게 된다.

void delete_case6
 struct node *s = sibling;

 s->color = n->parent->color;
 n->parent->color = BLACK;

 if  {
 s->right->color = BLACK;
 rotate_left;
 s->left->color = BLACK;
 rotate_right;

case 2를 통과하면 **N**과 **S**는 반드시 검은색 노드가 된다. case 2를 통과한 후에 남는 경우의 수는 아래와 같은데, 아래의 경우 모두 각각의 case에서 해결된다.
 =  - case 3에서 해결,  - case 4에서 해결,  - case 5에서 해결,  - case 5에서 해결,  - case 6에서 해결,  - case 6에서 해결,  - case 6에서 해결,  - case 6에서 해결. case 1-6까지의 경우를 다 지나면 위와 같이 모든 경우의 수에 대해 커버됨을 확인할 수 있다. 

다시 이야기하지만, 함수 콜은 모두  을 이용하므로, 알고리즘은 이다.
위의 알고리즘에서, 부모 노드에 대해 case 1으로 재귀하는 delete case 3을 제외하고는 모든 case에 대해 순서대로 chained되어 있다: 이 경우가 in-place 구현에서 유일하게 효과적으로 루프를 도는 case이다.

추가적으로, 자식 노드에 대해서는 tail recursion이 일어나지 않으므로, tail recursion 루프는 자식 노드에서부터 연결된 조상 노드들로 거꾸로 올라간다. case 1로 돌아가는 루프는 O번을 넘지 않는다. 만약 case 2에서 회전이 일어나면, **N**의 부모노드는 회전 후 빨강으로 변하고, 루프에서 빠져나온다. 그러므로 이 루프 내에서는 회전은 기껏해야 한 번 있다. 루프를 빠져나오고 나서는 두 번 보다 많은 횟수의 추가적인 회전은 일어나지 않기 때문에 모두 합쳐서 3번보다 많은 수의 회전은 일어나지 않는다.

## 병렬 알고리즘 
아이템의 수에 비례하여 컴퓨터 프로세서를 이용할 수 있다면, 정렬되어 있는 아이템을 가지고 레드-블랙 트리를 만드는 병렬 알고리즘은 상수시간이나