# 2025q1 Quiz 5,6 ## Q3 ### 目標 - 了解rb_tree insertion, deletion - map 實作 [完整程式](https://github.com/Brianpan/kernel-quiz56/blob/master/q3.c) ### 插入節點 插入節點主要分成三個步驟 1. 找到插入節點的位置 2. 插入節點 3. 旋轉,重新著色紅黑數 ```cpp= map_node_t **pnode; rb_link_node(node, parent, pnode); rb_insert_color(node, &map->root); ``` 內迴圈if/else邏輯是對稱,差別只在左右節點 情形一 叔叔節點是紅色 ```graphviz digraph { G[fontcolor="BLACK"] P[fontcolor="RED"] U[fontcolor="RED"] N G->P G->U P->N } ``` 進行顏色對調並且往上再平衡 ```cpp= node = gparent ``` ```graphviz digraph { G[fontcolor="RED" label="G (new N)"] P[fontcolor="BLACK"] U[fontcolor="BLACK"] N[fontcolor="RED"] G->P G->U P->N } ``` 情形二 叔叔節點是黑色 2-1 : zig-zag情形 ```graphviz digraph { G[fontcolor="RED"] P[fontcolor="BLACK"] U[fontcolor="BLACK"] N[fontcolor="RED" label="N (right child)"] G->P G->U P->N } ``` 親代節點左旋轉 ```graphviz digraph { G[fontcolor="RED"] P[label="P" fontcolor="BLACK"] a0[label="N" fontcolor="RED"] a1[label="U" fontcolor="BLACK"] G->a0 G->a1 a0->P } ``` 2-2 右旋轉祖父節點 ```graphviz digraph { G[label="G (left child) "fontcolor="RED"] P[label="P" fontcolor="RED"] a1[label="U" fontcolor="BLACK"] a0[label="N (right child)" fontcolor="BLACK"] a1->G G->a0 a0->P } ``` ```cpp= void rb_insert_color(struct rb_node *node, struct rb_root *root) { struct rb_node *parent, *gparent; while ((parent = rb_parent(node)) && rb_is_red(parent)) { gparent = rb_parent(parent); if (parent == gparent->rb_left) { { struct rb_node *uncle = gparent->rb_right; if (uncle && rb_is_red(uncle)) { rb_set_black(uncle); rb_set_black(parent); rb_set_red(gparent); node = gparent; continue; } } if (parent->rb_right == node) { __rb_rotate_left(parent, root); struct rb_node *tmp = parent; parent = node; node = tmp; } rb_set_black(parent); rb_set_red(node); __rb_rotate_right(gparent, root); } else { { struct rb_node *uncle = gparent->rb_left; if (uncle && rb_is_red(uncle)) { rb_set_black(uncle); rb_set_black(parent); rb_set_red(gparent); node = gparent; continue; } } if (parent->rb_left == node) { __rb_rotate_right(parent, root); struct rb_node *tmp = parent; parent = node; node = tmp; } rb_set_black(parent); rb_set_red(node); __rb_rotate_left(gparent, root); } } rb_set_black(root->rb_node); } ``` ### 刪除節點 ### 刪除節點主函數 這裡可以參考`__rb_erase_augmented`的解說,對應到核心的程式碼 主要判斷條件 - 要刪除的節點是否有兩個子節點 - 有 ```graphviz digraph { G[fontcolor="BLACK"] p0[label="x" fontcolor="BLACK"] p1[label="P" fontcolor="BLACK"] u[label="U" fontcolor="BLACK"] n[label="N" fontcolor="BLACK"] G->p0 G->p1 p1->u p1->n n->c0 n->c1 c1->a0 c1->a1 a0->t0 a0->t1 } ``` 以此圖為例,假設刪除節點N 找到右子樹的最小節點t0取代N 如果t0有右子樹, 變成原本親代節點的左子樹 - 沒有 直接把剩餘子節點取代刪除節點 ```graphviz digraph { G[fontcolor="BLACK"] p0[label="x" fontcolor="BLACK"] p1[label="P" fontcolor="BLACK"] u[label="U" fontcolor="BLACK"] n[label="N" fontcolor="BLACK"] G->p0 G->p1 p1->u p1->n n->c1 c1->a0 c1->a1 a0->t0 a0->t1 } ``` 此圖為例,直接讓c1當節點P的右子節點 如果節點node是黑色節點 需要進行樹的再平衡 ```cpp= void rb_erase(struct rb_node *node, struct rb_root *root) { struct rb_node *child, *parent; int color; if (!node->rb_left) child = node->rb_right; else if (!node->rb_right) child = node->rb_left; // has 2 children else { struct rb_node *old = node, *left; node = node->rb_right; // node becomes smallest node in the right subtree while ((left = node->rb_left)) node = left; // replace orginal node(old) by node if (rb_parent(old)) { if (rb_parent(old)->rb_left == old) rb_parent(old)->rb_left = node; else rb_parent(old)->rb_right = node; } else { root->rb_node = node; } child = node->rb_right; parent = rb_parent(node); color = rb_color(node); // case: original node has no left child if (parent == old) { parent = node; } else { if (child) rb_set_parent(child, parent); parent->rb_left = child; node->rb_right = old->rb_right; rb_set_parent(old->rb_right, node); } node->rb_parent_color = old->rb_parent_color; node->rb_left = old->rb_left; rb_set_parent(old->rb_left, node); goto color; } parent = rb_parent(node); color = rb_color(node); if (child) rb_set_parent(child, parent); if (parent) { if (parent->rb_left == node) parent->rb_left = child; else parent->rb_right = child; } else { root->rb_node = child; } color: if (color == RB_BLACK) __rb_erase_color(child, parent, root); } ``` 刪除節點的再平衡 if/else一樣是判斷是左節點還是右節點,看一邊即可 兄弟節點other是紅色,對親代節左旋轉並且重新著色 other->黑色 parent->紅色 - other節點所有子節點是黑色節點 變更other節點成紅色節點 要往上重新調整因為other親代節點的左右子節點不符合紅黑數條件 other節點的左節點是紅色,右節點是黑色 ```graphviz digraph { p[label="P" fontcolor="RED"] n[label="N" fontcolor="BLACK"] o[label="o" fontcolor="BLACK"] l[label="l" fontcolor="RED"] r[label="b" fontcolor="BLACK"] p->n p->o o->l o->r } ``` 對other右旋轉並且重新著色,改變other指標指到l ```graphviz digraph { p[label="P" fontcolor="RED"] n[label="N" fontcolor="BLACK"] l[label="l" fontcolor="BLACK"] o[label="o" fontcolor="RED"] r[label="b" fontcolor="BLACK"] p->n p->l l->o o->r } ``` 結束上述條件判斷後 ```cpp= rb_set_color(other, rb_color(parent)); rb_set_black(parent); rb_set_black(other->rb_left); __rb_rotate_left(parent, root); node = root->rb_node; break; ``` 就是把parent左旋轉並且顏色對調 ```graphviz digraph { o[label="O" fontcolor="RED"] p[label="P" fontcolor="BLACK"] n[label="N" fontcolor="BLACK"] x y o->p o->x x->y p->n } ``` ```cpp= static void __rb_erase_color(struct rb_node *node, struct rb_node *parent, struct rb_root *root) { struct rb_node *other; while ((!node || rb_is_black(node)) && node != root->rb_node) { if (parent->rb_left == node) { other = parent->rb_right; if (rb_is_red(other)) { rb_set_black(other); rb_set_red(parent); __rb_rotate_left(parent, root); other = parent->rb_right; } if ((!other->rb_left || rb_is_black(other->rb_left)) && (!other->rb_right || rb_is_black(other->rb_right))) { rb_set_red(other); node = parent; parent = rb_parent(node); } else { if (!other->rb_right || rb_is_black(other->rb_right)) { rb_set_black(other->rb_left); rb_set_red(other); __rb_rotate_right(other, root); other = parent->rb_right; } rb_set_color(other, rb_color(parent)); rb_set_black(parent); rb_set_black(other->rb_right); __rb_rotate_right(parent, root); node = root->rb_node; break; } } else { other = parent->rb_left; if (rb_is_red(other)) { rb_set_black(other); rb_set_red(parent); __rb_rotate_right(parent, root); other = parent->rb_left; } if ((!other->rb_left || rb_is_black(other->rb_left)) && (!other->rb_right || rb_is_black(other->rb_right))) { rb_set_red(other); node = parent; parent = rb_parent(node); } else { if (!other->rb_left || rb_is_black(other->rb_left)) { rb_set_black(other->rb_right); rb_set_red(other); __rb_rotate_left(other, root); other = parent->rb_left; } rb_set_color(other, rb_color(parent)); rb_set_black(parent); rb_set_black(other->rb_left); __rb_rotate_left(parent, root); node = root->rb_node; break; } } } if (node) rb_set_black(node); } ```