Algorithm - HW2 110550027 管培勛 Runtime / Environment The code is written in C++, compiling by g++ (version is g++ (Ubuntu 9.4.0-1ubuntu1~20.04.1) 9.4.0 ), and runs on my Ubuntu 20.04 VM No IDE is used, I only use vim to write and use g++ command to compile Results I use struct to construct a node, and it contains its value ( val ), color ( color ), left ( L ) and right ( R ) children and parent ( parent ). We discuss insertion and deletion separately. RB-Tree has some rules 1. root is black 2. node->color == red → node->L->color == black && node->R->color == black (when children exist), but the reverse way does not hold 3. when the children do not exist, we regard it as black 4. any path from root to leaf (or empty leaf) has the same number of black nodes, we denote it as β Insertion We first create a node and add it to the tree. A new node is colored red by default. Recall that in BST, we go through the tree and find a proper place for the new node, and then we insert it. However, normal BST do not store parent , but RB-Tree does, so we have to update new_node->parent , new_node>parent->L or new_node->parent->R (depends on the path it goes down). Next, we need to do balance . We explain the balance function, we only consider the subtree containing grand = new_node->parent->parent , parent = new_node->parent , uncle is the other node beside parent 1. when the new node do not have parent, it means that it is root , so we assign it to root and change its color to black 2. when parent is black, we need not do anything since the new node which is red will not violate any rule (red node does affect β) 3. when parent is red 1. when parent and uncle are both red (imply grand is black), then we can exchange the colors of them, i.e., parent->color = uncle->color = black and grand->color = red Since we just exchange the colors of two layers, β will not change, and rule 2 is also solved 2. when parent is red but uncle is black (black or empty), we discuss four (2 × 2) cases (but only explain the first since the others are similar) 1. grand->L->L == new_node We know that grand is common for parent and uncle , so the color does not matter in this subtree. Now, a new node which is red connects to a red node parent , it violates rule 2, so we have to make parent black, and to maintain β, grand should be red Here, we find that the uncle subtree has one fewer black nodes count since grand is recolored to red, but parent subtree still has the same black nodes count, so we make a right_rotate to the parent node However, we cannot make sure whether the whole tree is balanced, so we do balance again. 2. grand->R->R == new_node 3. grand->L->R == new_node 4. grand->R->L == new_node Deletion Recall that in BST deletion, we have three cases to discuss 1. remove leaf, just remove it and nothing more to do 2. remove a node with one children, move its children up ( parent->child = node->child ) 3. remove a node with two children (the most complicated case), find the max in left subtree LR (or min in right subtree, but the visualization tool does not use this and it produce different results, so I choose to use its way and I can debug with the tool) and replace the current node with it, and finally remove LR We have similar discussion in RB-Tree 1. when removing a node with two children We find the left max LR , and replace node with LR Next, we turn to removing LR , then the problem becomes removing a leaf, so we can continue 2. when removing a node with one children We have four cases for (node, child) : (R,B),(R,R),(B,B),(B,R) , and we can find that only (B,R) is possible, the reason is: If the child is black, then rule 4 is violated, and (R,R) is impossible by rule 2 So in (B,R) case, we have to change the child to black to take the position of node 3. when removing a leaf 1. if the leaf is red, then just remove it, no rule will be violated 2. if the leaf is black, we need to discuss since it may affect β. We extract this part of code to another function del_balance We have two cases: node == parent->L and node == parent->R , and they have symmetry, so we only explain the first one We let sibling be the node beside node , and L = sibling->L , R = sibling->R 1. sibling->color == red , we have to do rotation for parent since when node is deleted, rule 4 will be violated. So sibling will take the position of parent , and we need to parent->color = red and sibling>color = black (exchange colors) 2. sibling (from above else if ), sibling->R and sibling->L are all black (or empty), since the removal of node will decrease the number of black nodes, so sibling has to be colored red Then we consider when parent->color == red , we can maintain β by change it to black, and the balance is done Otherwise, we cannot do anything and have to do balance again (do not break ) starting at parent 3. The case is sibling->color == R->color == black and L->color == red We make L which is red up, and keep balancing 4. when sibling->color == black and R->color == red . Since node is black and should be deleted, we need a node to take on its position, so we need to do left_rotation to parent , and make parent black And now sibling is new parent, it should have the color parent originally have Also, R left on right subtree after rotation, so it must be black to avoid violating rule 4 By doing so, the rules are all met, so we can stop balancing References It is so hard to complete it only by my own, so I list some references 1. JavaScript 學演算法(十八)- 紅黑樹(下):刪除操作 – 竹白記事本 (chupai.github.io) 2. 1.4.3 Red-Black Tree - 資料結構&演算法筆記 (gitbook.io) 3. Red Black Tree: Delete(刪除資料)與Fixup(修正) (alrightchiu.github.io) 4. Red/Black Tree Visualization (usfca.edu) 5. My DS/OOP homework about BST and AVL 6. textbook