接着上一篇我们就只剩下了红黑树的删除了,这也是较为复杂的操作(原理一套gif(只是简单部分),代码两套gif(困难部分博主会从头讲到尾)),因为删除操作比较复杂,所以博主打算简单一套,复杂一套,希望大家看了博主的博客以后不要在惧怕红黑树了!!!

由于GIF大小有限制所以想要看比较清楚的可以点击下面的连接进行观看。

博主为了让大家认识更清楚所以在每套GIF都会给出代码(虽然会显得比较冗长,但是你能坚持看下来的效果一定是非常好的!)
其实删除的话我们是分为两个部分
(1)删除
(2)恢复

删除:

当你要删除一个点的时候又分为下面的情况 删除时候的情况: 1. 如果要被删除的节点没有孩子,那么就直接删除。 2. 如果删除的节点有一个孩子,删除之后,用它的孩子还代替他。 3. 如果有两个孩子,这个时候你可以选择左孩子那条路径里最大的值,或者右孩子最小的值来进行删除,然后这个孩子去替代原来节点的位置 (NGINX是选择用右孩子的最小值来进行删除)。以这个结点的键与值(key与value/data)替换待删结点的键与值,然后删除这个替身

删除操作总体来说很简单,就是要保证被删除的节点只有一个或者没有孩子就行了。

恢复

恢复主要分为几种情况(如果是红色,就直接返回,因为没有影响到红黑树的性质 删除后,被删除的节点的孩子会顶替到删除节点的位置,孩子节点我们记为temp因为恢复操作就是围绕着temp来进行的)咱们只讨论左孩子,右孩子镜像。

1.如果temp的兄弟节点w为红色。那么他们的父亲节点为黑色

处理办法:把w弄成黑色,把parent弄成红色进行一次左旋,然后将情况转换为后面一种已知的情况 然后将temp指向temp的兄弟。

2.如果temp的兄弟w为黑色,这个时候他们的父亲节点就可红可黑了,因为temp这条路径是比他兄弟那条路径少一个黑色的节点,
如果这个时候他兄弟节点w的两个孩子都是黑色的,那么咱们就直接把w节点设置为红色,这个时候两个兄弟节点的黑色数量一样,但是w parent这条路劲上的黑色节点少一个所以指向w的parent继续循环。

3.如果temp的兄弟节点w为黑色,并且他的右孩子也是黑色,左孩子为红色,那么我们就将w的左孩子设置为黑色,把设置为红色,并且以w进行一次右旋,转换为情况4.

4.如果temp的兄弟节点w为黑色,并且他的左孩子为黑色,右孩子为红色,这时候就把w的颜色设置为parent的颜色,parent设置为黑色,w的右孩子设置为黑色,然后以parent为节点进行右旋,恢复红黑树的性质。

下面我们就只给出删除的代码:

//红黑树的删除!!!!!//据说红黑树和AVL树的区别主要体现在删除节点时,我们就来看一看。
void
ngx_rbtree_delete(ngx_rbtree_t *tree, ngx_rbtree_node_t *node)
{ngx_uint_t           red;ngx_rbtree_node_t  **root, *sentinel, *subst, *temp, *w;/* a binary tree delete */root = &tree->root;             //树根指针的指针赋给了rootsentinel = tree->sentinel;      //哨兵指针赋给了哨兵指针/* 下面是获取temp节点值,temp保存的节点是准备替换节点node ;* subst是保存要被替换的节点的后继节点;*//* case1:若node节点没有左孩子(这里包含了存在或不存在右孩子的情况)*/if (node->left == sentinel) {  //如果左子结点是哨兵或左右子结点都是哨兵temp = node->right;            //获得右子结点,后面让它接替node位置subst = node;              //node赋给subst} /* case2:node节点存在左孩子,但是不存在右孩子 */else if (node->right == sentinel) {   //如果右子结点是哨兵temp = node->left;           //获得左子结点,后面让它接替node位置subst = node;          //node赋给subst/* case3:node节点既有左孩子,又有右孩子 */} else {    //如果左右子结点都不是哨兵/* 获取node节点的后续节点 */subst = ngx_rbtree_min(node->right, sentinel); //获得右子树中最小的结点---->头结点里面有一个内联函数来着if (subst->left != sentinel) {   //如果右子树的最小结点的左子结点不是哨兵,基本上会不执行这条语句temp = subst->left;        //获得右子树的最小结点的左子结点} else {               //否则获得右子树最小结点的右子结点temp = subst->right;      //看起来subst将被从原位置删掉然后接替node的位置}}//下面我们来看看temp和subst要干什么用:/* 若被替换的节点subst是根节点,则temp直接替换subst成为根节点 */if (subst == *root) {     //如果subst是根  --->//真正删除的节点是根节点*root = temp;         //temp接替根ngx_rbt_black(temp);   //染黑temp/* DEBUG stuff */node->left = NULL;     //清空了待删结点node->right = NULL;node->parent = NULL;node->key = 0;return;}//将我们的后继节点从书上脱离出来/* red记录subst节点的颜色 */red = ngx_rbt_is_red(subst);   //获得subst是否是红色/* temp节点替换subst 节点 */if (subst == subst->parent->left) {     //如果subst是左子结点subst->parent->left = temp;            //把接替结点挂到subst位置} else {        //如果subst是右子结点subst->parent->right = temp;   //把接替结点挂到subst位置}/* 根据subst是否为node节点进行处理 */if (subst == node) {       //如果subst是待删结点--->//需要删除的节点是本身temp->parent = subst->parent;   //接替结点直接接替,删除完成} else {      //如果subst不是待删结点if (subst->parent == node) {    //如果subst的父结点就是待删结点temp->parent = subst;        //接替结点挂在subst上} else {      {//如果待删结点比subst的父结点更高temp->parent = subst->parent;   //把接替结点挂在subst的父结点上 -->//常规情况}/* 复制node节点属性 *///subst接替待删结点node的位置,复制待删结点跟周围结点的关系     --->把node的信息拷贝到subst里面去subst->left = node->left;subst->right = node->right;subst->parent = node->parent;ngx_rbt_copy_color(subst, node);if (node == *root) {  //如果是root节点 //如果待删结点是根*root = subst;       //subst接替根} else {      //如果待删结点不是根,subst接替它        -->维护父子信息if (node == node->parent->left) {node->parent->left = subst;} else {node->parent->right = subst;}}//这里就是将node完全脱离我们的红黑树了if (subst->left != sentinel) {     //如果subst左子结点不是哨兵subst->left->parent = subst;    //subst的左子结点放弃node,挂上来}if (subst->right != sentinel) {       //如果subst右子结点不是哨兵subst->right->parent = subst;   //subst右子结点放弃node,挂上来}}//清空待删结点node/* DEBUG stuff */node->left = NULL;node->right = NULL;node->parent = NULL;node->key = 0;//如果subst是红色,红黑树约束依然被遵守,删除工作就可以结束了if (red) {return;}/* 下面开始是调整红黑树的性质 *///看起来结点的删除过程已经顺利完成了,但是如果subst是黑色,我们需要修复红黑树的约束。//下面这一段代码的主角是接替subst位置的temp结点:/* a delete fixup *///当subst的接替结点不是根且为黑色,循环/* 根据temp节点进行处理 ,若temp不是根节点且为黑色 */while (temp != *root && ngx_rbt_is_black(temp)) {/* 若temp是其父亲节点的左孩子 */if (temp == temp->parent->left) { //如果temp是左子结点w = temp->parent->right;    //获得其右兄弟        /* w为temp的兄弟节点 *//* case A:temp兄弟节点为红色 *//* 解决办法:* 1、改变w节点及temp父亲节点的颜色;* 2、对temp父亲节的做一次左旋转,此时,temp的兄弟节点是旋转之前w的某个子节点,该子节点颜色为黑色;* 3、此时,case A已经转换为case B、case C 或 case D;*/if (ngx_rbt_is_red(w)) {    //如果temp的右兄弟是红色ngx_rbt_black(w);        //染黑temp的右兄弟ngx_rbt_red(temp->parent);   //染红temp的父结点ngx_rbtree_left_rotate(root, sentinel, temp->parent);    //temp的父结点左旋w = temp->parent->right; //获得temp的新右兄弟}/* case B:temp的兄弟节点w是黑色,且w的两个子节点都是黑色 *//* 解决办法:* 1、改变w节点的颜色;* 2、把temp的父亲节点作为新的temp节点;*/if (ngx_rbt_is_black(w->left) && ngx_rbt_is_black(w->right)) {     //如果temp右兄弟的左右子结点都是黑的ngx_rbt_red(w);   //染红temp的右兄弟temp = temp->parent;    //获得temp的父结点为新temp} else {  //如果temp右兄弟的子结点不全为黑/* case C:temp的兄弟节点是黑色,且w的左孩子是红色,右孩子是黑色 *//* 解决办法:* 1、将改变w及其左孩子的颜色;* 2、对w节点进行一次右旋转;* 3、此时,temp新的兄弟节点w有着一个红色右孩子的黑色节点,转为case D;*/if (ngx_rbt_is_black(w->right)) {   //如果其右子结点是黑色ngx_rbt_black(w->left);  //染黑左子结点ngx_rbt_red(w); //染红temp的右兄弟ngx_rbtree_right_rotate(root, sentinel, w); //右兄弟右旋w = temp->parent->right;  //获得temp的新右兄弟}/* case D:temp的兄弟节点w为黑色,且w的右孩子为红色 *//* 解决办法:* 1、将w节点设置为temp父亲节点的颜色,temp父亲节点设置为黑色;* 2、w的右孩子设置为黑色;* 3、对temp的父亲节点做一次左旋转;* 4、最后把根节点root设置为temp节点;*/ngx_rbt_copy_color(w, temp->parent);    //temp右兄弟复制temp父结点颜色ngx_rbt_black(temp->parent); //染黑temp父结点ngx_rbt_black(w->right);  //染黑temp右兄弟的右子结点ngx_rbtree_left_rotate(root, sentinel, temp->parent);     //temp父结点左旋temp = *root;      //获得根}/* 这里针对的是temp节点为其父亲节点的左孩子的情况 */} else{//如果temp是右子结点,做对称的事 w = temp->parent->left;if (ngx_rbt_is_red(w)) {ngx_rbt_black(w);ngx_rbt_red(temp->parent);ngx_rbtree_right_rotate(root, sentinel, temp->parent);w = temp->parent->left;}if (ngx_rbt_is_black(w->left) && ngx_rbt_is_black(w->right)) {ngx_rbt_red(w);temp = temp->parent;} else {if (ngx_rbt_is_black(w->left)) {ngx_rbt_black(w->right);ngx_rbt_red(w);ngx_rbtree_left_rotate(root, sentinel, w);w = temp->parent->left;}ngx_rbt_copy_color(w, temp->parent);ngx_rbt_black(temp->parent);ngx_rbt_black(w->left);ngx_rbtree_right_rotate(root, sentinel, temp->parent);temp = *root;}}}ngx_rbt_black(temp);  //染黑当前temp
}
   如果大家感觉文字解释依然不好理解的话,博主依然给大家做了 NGINX红黑树的删除gif(分为两个部分,比较简单的,和复杂的情况)下面我们还是将删除分为我们之前所说的两个部分吧。

删除部分的代码(原理篇)简单部分(删除红色节点,不需要恢复)

//红黑树的删除!!!!!//据说红黑树和AVL树的区别主要体现在删除节点时,我们就来看一看。
void
ngx_rbtree_delete(ngx_rbtree_t *tree, ngx_rbtree_node_t *node)
{ngx_uint_t           red;ngx_rbtree_node_t  **root, *sentinel, *subst, *temp, *w;/* a binary tree delete */root = &tree->root;             //树根指针的指针赋给了rootsentinel = tree->sentinel;      //哨兵指针赋给了哨兵指针/* 下面是获取temp节点值,temp保存的节点是准备替换节点node ;* subst是保存要被替换的节点的后继节点;*//* case1:若node节点没有左孩子(这里包含了存在或不存在右孩子的情况)*/if (node->left == sentinel) {  //如果左子结点是哨兵或左右子结点都是哨兵temp = node->right;            //获得右子结点,后面让它接替node位置subst = node;              //node赋给subst} /* case2:node节点存在左孩子,但是不存在右孩子 */else if (node->right == sentinel) {   //如果右子结点是哨兵temp = node->left;           //获得左子结点,后面让它接替node位置subst = node;          //node赋给subst/* case3:node节点既有左孩子,又有右孩子 */} else {    //如果左右子结点都不是哨兵/* 获取node节点的后续节点 */subst = ngx_rbtree_min(node->right, sentinel); //获得右子树中最小的结点---->头结点里面有一个内联函数来着if (subst->left != sentinel) {   //如果右子树的最小结点的左子结点不是哨兵,基本上会不执行这条语句temp = subst->left;        //获得右子树的最小结点的左子结点} else {               //否则获得右子树最小结点的右子结点temp = subst->right;      //看起来subst将被从原位置删掉然后接替node的位置}}//下面我们来看看temp和subst要干什么用:/* 若被替换的节点subst是根节点,则temp直接替换subst成为根节点 */if (subst == *root) {     //如果subst是根  --->//真正删除的节点是根节点*root = temp;         //temp接替根ngx_rbt_black(temp);   //染黑temp/* DEBUG stuff */node->left = NULL;     //清空了待删结点node->right = NULL;node->parent = NULL;node->key = 0;return;}//将我们的后继节点从书上脱离出来/* red记录subst节点的颜色 */red = ngx_rbt_is_red(subst);   //获得subst是否是红色/* temp节点替换subst 节点 */if (subst == subst->parent->left) {     //如果subst是左子结点subst->parent->left = temp;            //把接替结点挂到subst位置} else {        //如果subst是右子结点subst->parent->right = temp;   //把接替结点挂到subst位置}/* 根据subst是否为node节点进行处理 */if (subst == node) {       //如果subst是待删结点--->//需要删除的节点是本身temp->parent = subst->parent;   //接替结点直接接替,删除完成} else {      //如果subst不是待删结点if (subst->parent == node) {    //如果subst的父结点就是待删结点temp->parent = subst;        //接替结点挂在subst上} else {      {//如果待删结点比subst的父结点更高temp->parent = subst->parent;   //把接替结点挂在subst的父结点上 -->//常规情况}/* 复制node节点属性 *///subst接替待删结点node的位置,复制待删结点跟周围结点的关系     --->把node的信息拷贝到subst里面去subst->left = node->left;subst->right = node->right;subst->parent = node->parent;ngx_rbt_copy_color(subst, node);if (node == *root) {  //如果是root节点 //如果待删结点是根*root = subst;       //subst接替根} else {      //如果待删结点不是根,subst接替它        -->维护父子信息if (node == node->parent->left) {node->parent->left = subst;} else {node->parent->right = subst;}}//这里就是将node完全脱离我们的红黑树了if (subst->left != sentinel) {     //如果subst左子结点不是哨兵subst->left->parent = subst;    //subst的左子结点放弃node,挂上来}if (subst->right != sentinel) {       //如果subst右子结点不是哨兵subst->right->parent = subst;   //subst右子结点放弃node,挂上来}}//清空待删结点node/* DEBUG stuff */node->left = NULL;node->right = NULL;node->parent = NULL;node->key = 0;//如果subst是红色,红黑树约束依然被遵守,删除工作就可以结束了if (red) {return;}

GIF

图片

删除部分的代码(代码篇)简单部分(删除红色节点,不需要恢复)

//红黑树的删除!!!!!//据说红黑树和AVL树的区别主要体现在删除节点时,我们就来看一看。
void
ngx_rbtree_delete(ngx_rbtree_t *tree, ngx_rbtree_node_t *node)
{ngx_uint_t           red;ngx_rbtree_node_t  **root, *sentinel, *subst, *temp, *w;/* a binary tree delete */root = &tree->root;             //树根指针的指针赋给了rootsentinel = tree->sentinel;      //哨兵指针赋给了哨兵指针/* 下面是获取temp节点值,temp保存的节点是准备替换节点node ;* subst是保存要被替换的节点的后继节点;*//* case1:若node节点没有左孩子(这里包含了存在或不存在右孩子的情况)*/if (node->left == sentinel) {  //如果左子结点是哨兵或左右子结点都是哨兵temp = node->right;            //获得右子结点,后面让它接替node位置subst = node;              //node赋给subst} /* case2:node节点存在左孩子,但是不存在右孩子 */else if (node->right == sentinel) {   //如果右子结点是哨兵temp = node->left;           //获得左子结点,后面让它接替node位置subst = node;          //node赋给subst/* case3:node节点既有左孩子,又有右孩子 */} else {    //如果左右子结点都不是哨兵/* 获取node节点的后续节点 */subst = ngx_rbtree_min(node->right, sentinel); //获得右子树中最小的结点---->头结点里面有一个内联函数来着if (subst->left != sentinel) {   //如果右子树的最小结点的左子结点不是哨兵,基本上会不执行这条语句temp = subst->left;        //获得右子树的最小结点的左子结点} else {               //否则获得右子树最小结点的右子结点temp = subst->right;      //看起来subst将被从原位置删掉然后接替node的位置}}//下面我们来看看temp和subst要干什么用:/* 若被替换的节点subst是根节点,则temp直接替换subst成为根节点 */if (subst == *root) {     //如果subst是根  --->//真正删除的节点是根节点*root = temp;         //temp接替根ngx_rbt_black(temp);   //染黑temp/* DEBUG stuff */node->left = NULL;     //清空了待删结点node->right = NULL;node->parent = NULL;node->key = 0;return;}//将我们的后继节点从书上脱离出来/* red记录subst节点的颜色 */red = ngx_rbt_is_red(subst);   //获得subst是否是红色/* temp节点替换subst 节点 */if (subst == subst->parent->left) {     //如果subst是左子结点subst->parent->left = temp;            //把接替结点挂到subst位置} else {        //如果subst是右子结点subst->parent->right = temp;   //把接替结点挂到subst位置}/* 根据subst是否为node节点进行处理 */if (subst == node) {       //如果subst是待删结点--->//需要删除的节点是本身temp->parent = subst->parent;   //接替结点直接接替,删除完成} else {      //如果subst不是待删结点if (subst->parent == node) {    //如果subst的父结点就是待删结点temp->parent = subst;        //接替结点挂在subst上} else {      {//如果待删结点比subst的父结点更高temp->parent = subst->parent;   //把接替结点挂在subst的父结点上 -->//常规情况}/* 复制node节点属性 *///subst接替待删结点node的位置,复制待删结点跟周围结点的关系     --->把node的信息拷贝到subst里面去subst->left = node->left;subst->right = node->right;subst->parent = node->parent;ngx_rbt_copy_color(subst, node);if (node == *root) {  //如果是root节点 //如果待删结点是根*root = subst;       //subst接替根} else {      //如果待删结点不是根,subst接替它        -->维护父子信息if (node == node->parent->left) {node->parent->left = subst;} else {node->parent->right = subst;}}//这里就是将node完全脱离我们的红黑树了if (subst->left != sentinel) {     //如果subst左子结点不是哨兵subst->left->parent = subst;    //subst的左子结点放弃node,挂上来}if (subst->right != sentinel) {       //如果subst右子结点不是哨兵subst->right->parent = subst;   //subst右子结点放弃node,挂上来}}//清空待删结点node/* DEBUG stuff */node->left = NULL;node->right = NULL;node->parent = NULL;node->key = 0;//如果subst是红色,红黑树约束依然被遵守,删除工作就可以结束了if (red) {return;}

GIF:(实在是不行,所以只有传小图了!!!)

图片:



虽然博主上面说的是 简单的,但是也是给大家选的有两个分支进行删除,这也是为了我们后面复杂部分而准备的,复杂部分的删除和我们之前的是一样的(复杂的删除掌握了,简单的应该是没问题的),所以我们在复杂片就给出大家恢复的策略!!!



恢复(按照小曼四步执行,即可恢复红黑树)

恢复主要分为几种情况(如果是红色,就直接返回,因为没有影响到红黑树的性质 删除后,被删除的节点的孩子会顶替到删除节点的位置(其实就是我们要后继节点(右子树的最小值)),孩子节点我们记为temp因为恢复操作就是围绕着temp来进行的)咱们只讨论左孩子,右孩子镜像。

下面是要被删除节点的结点是黑色的情况:

1.如果temp的兄弟节点w为红色。那么他们的父亲节点为黑色

处理办法:把w弄成黑色,把parent弄成红色进行一次左旋,然后将情况转换为后面一种已知的情况 然后将temp指向temp的兄弟。

2.如果temp的兄弟w为黑色,这个时候他们的父亲节点就可红可黑了,因为temp这条路径是比他兄弟那条路径少一个黑色的节点,
如果这个时候他兄弟节点w的两个孩子都是黑色的,那么咱们就直接把w节点设置为红色,这个时候两个兄弟节点的黑色数量一样,但是w parent这条路劲上的黑色节点少一个所以指向w的parent继续循环。

3.如果temp的兄弟节点w为黑色,并且他的右孩子也是黑色,左孩子为红色,那么我们就将w的左孩子设置为黑色,把设置为红色,并且以w进行一次右旋,转换为情况4.

4.如果temp的兄弟节点w为黑色,并且他的左孩子为黑色,右孩子为红色,这时候就把w的颜色设置为parent的颜色,parent设置为黑色,w的右孩子设置为黑色,然后以parent为节点进行右旋,恢复红黑树的性质。

那我们就按照复杂的情况来恢复吧。

恢复的代码(代码篇)(复杂部分完整版,包含左右旋)

void
ngx_rbtree_delete(ngx_rbtree_t *tree, ngx_rbtree_node_t *node)
{ngx_uint_t           red;ngx_rbtree_node_t  **root, *sentinel, *subst, *temp, *w;/* a binary tree delete */root = &tree->root;             //树根指针的指针赋给了rootsentinel = tree->sentinel;      //哨兵指针赋给了哨兵指针/* 下面是获取temp节点值,temp保存的节点是准备替换节点node ;* subst是保存要被替换的节点的后继节点;*//* case1:若node节点没有左孩子(这里包含了存在或不存在右孩子的情况)*/if (node->left == sentinel) {  //如果左子结点是哨兵或左右子结点都是哨兵temp = node->right;            //获得右子结点,后面让它接替node位置subst = node;              //node赋给subst} /* case2:node节点存在左孩子,但是不存在右孩子 */else if (node->right == sentinel) {   //如果右子结点是哨兵temp = node->left;           //获得左子结点,后面让它接替node位置subst = node;          //node赋给subst/* case3:node节点既有左孩子,又有右孩子 */} else {    //如果左右子结点都不是哨兵/* 获取node节点的后续节点 */subst = ngx_rbtree_min(node->right, sentinel); //获得右子树中最小的结点---->头结点里面有一个内联函数来着if (subst->left != sentinel) {   //如果右子树的最小结点的左子结点不是哨兵,基本上会不执行这条语句temp = subst->left;        //获得右子树的最小结点的左子结点} else {               //否则获得右子树最小结点的右子结点temp = subst->right;      //看起来subst将被从原位置删掉然后接替node的位置}}//下面我们来看看temp和subst要干什么用:/* 若被替换的节点subst是根节点,则temp直接替换subst成为根节点 */if (subst == *root) {     //如果subst是根  --->//真正删除的节点是根节点*root = temp;         //temp接替根ngx_rbt_black(temp);   //染黑temp/* DEBUG stuff */node->left = NULL;     //清空了待删结点node->right = NULL;node->parent = NULL;node->key = 0;return;}//将我们的后继节点从书上脱离出来/* red记录subst节点的颜色 */red = ngx_rbt_is_red(subst);   //获得subst是否是红色  /* temp节点替换subst 节点 */if (subst == subst->parent->left) {       //如果subst是左子结点subst->parent->left = temp;            //把接替结点挂到subst位置} else {        //如果subst是右子结点subst->parent->right = temp;   //把接替结点挂到subst位置}/* 根据subst是否为node节点进行处理 */if (subst == node) {       //如果subst是待删结点--->//需要删除的节点是本身temp->parent = subst->parent;   //接替结点直接接替,删除完成} else {      //如果subst不是待删结点if (subst->parent == node) {    //如果subst的父结点就是待删结点temp->parent = subst;        //接替结点挂在subst上} else {      {//如果待删结点比subst的父结点更高temp->parent = subst->parent;   //把接替结点挂在subst的父结点上 -->//常规情况}/* 复制node节点属性 *///subst接替待删结点node的位置,复制待删结点跟周围结点的关系     --->把node的信息拷贝到subst里面去subst->left = node->left;subst->right = node->right;subst->parent = node->parent;ngx_rbt_copy_color(subst, node);if (node == *root) {  //如果是root节点 //如果待删结点是根*root = subst;       //subst接替根} else {      //如果待删结点不是根,subst接替它        -->维护父子信息if (node == node->parent->left) {node->parent->left = subst;} else {node->parent->right = subst;}}//这里就是将node完全脱离我们的红黑树了if (subst->left != sentinel) {     //如果subst左子结点不是哨兵subst->left->parent = subst;    //subst的左子结点放弃node,挂上来}if (subst->right != sentinel) {       //如果subst右子结点不是哨兵subst->right->parent = subst;   //subst右子结点放弃node,挂上来}}//清空待删结点node/* DEBUG stuff */node->left = NULL;node->right = NULL;node->parent = NULL;node->key = 0;//如果subst是红色,红黑树约束依然被遵守,删除工作就可以结束了if (red) {return;  //满足所以跳出!!!}/* 下面开始是调整红黑树的性质 *///看起来结点的删除过程已经顺利完成了,但是如果subst是黑色,我们需要修复红黑树的约束。//下面这一段代码的主角是接替subst位置的temp结点:/* a delete fixup *///当subst的接替结点(temp)不是根且为黑色,循环/* 根据temp节点进行处理 ,若temp不是根节点且为黑色 */while (temp != *root && ngx_rbt_is_black(temp)) {/* 若temp是其父亲节点的左孩子 */if (temp == temp->parent->left) {  //如果temp是左子结点w = temp->parent->right;    //获得其右兄弟        /* w为temp的兄弟节点 *//* case A:temp兄弟节点为红色 *//* 解决办法:* 1、改变w节点及temp父亲节点的颜色;* 2、对temp父亲节的做一次左旋转,此时,temp的兄弟节点是旋转之前w的某个子节点,该子节点颜色为黑色;* 3、此时,case A已经转换为case B、case C 或 case D;*/if (ngx_rbt_is_red(w)) {    //如果temp的右兄弟是红色ngx_rbt_black(w);        //染黑temp的右兄弟ngx_rbt_red(temp->parent);   //染红temp的父结点ngx_rbtree_left_rotate(root, sentinel, temp->parent);    //temp的父结点左旋w = temp->parent->right; //获得temp的新右兄弟}/* case B:temp的兄弟节点w是黑色,且w的两个子节点都是黑色 *//* 解决办法:* 1、改变w节点的颜色;* 2、把temp的父亲节点作为新的temp节点;*/if (ngx_rbt_is_black(w->left) && ngx_rbt_is_black(w->right)) {     //如果temp右兄弟的左右子结点都是黑的ngx_rbt_red(w);   //染红temp的右兄弟?temp = temp->parent;   //获得temp的父结点为新temp} else {  //如果temp右兄弟的子结点不全为黑/* case C:temp的兄弟节点是黑色,且w的左孩子是红色,右孩子是黑色 *//* 解决办法:* 1、将改变w及其左孩子的颜色;* 2、对w节点进行一次右旋转;* 3、此时,temp新的兄弟节点w有着一个红色右孩子的黑色节点,转为case D;*/if (ngx_rbt_is_black(w->right)) {   //如果其右子结点是黑色ngx_rbt_black(w->left);  //染黑左子结点ngx_rbt_red(w); //染红temp的右兄弟ngx_rbtree_right_rotate(root, sentinel, w); //右兄弟右旋w = temp->parent->right;  //获得temp的新右兄弟}/* case D:temp的兄弟节点w为黑色,且w的右孩子为红色 *//* 解决办法:* 1、将w节点设置为temp父亲节点的颜色,temp父亲节点设置为黑色;* 2、w的右孩子设置为黑色;* 3、对temp的父亲节点做一次左旋转;* 4、最后把根节点root设置为temp节点;*/ngx_rbt_copy_color(w, temp->parent);    //temp右兄弟复制temp父结点颜色ngx_rbt_black(temp->parent); //染黑temp父结点ngx_rbt_black(w->right);  //染黑temp右兄弟的右子结点ngx_rbtree_left_rotate(root, sentinel, temp->parent);     //temp父结点左旋temp = *root;      //获得根}/* 这里针对的是temp节点为其父亲节点的左孩子的情况 */} else{//如果temp是右子结点,做对称的事 w = temp->parent->left;if (ngx_rbt_is_red(w)) {ngx_rbt_black(w);ngx_rbt_red(temp->parent);ngx_rbtree_right_rotate(root, sentinel, temp->parent);w = temp->parent->left;}if (ngx_rbt_is_black(w->left) && ngx_rbt_is_black(w->right)) {ngx_rbt_red(w);temp = temp->parent;} else {if (ngx_rbt_is_black(w->left)) {ngx_rbt_black(w->right);ngx_rbt_red(w);ngx_rbtree_left_rotate(root, sentinel, w);w = temp->parent->left;}ngx_rbt_copy_color(w, temp->parent);ngx_rbt_black(temp->parent);ngx_rbt_black(w->left);ngx_rbtree_right_rotate(root, sentinel, temp->parent);temp = *root;}}}ngx_rbt_black(temp);  //染黑当前temp
}//左旋 就是以一个节点p和他的右孩子y为支轴进行,让y成为新的根,p成为y的左孩子,y的左孩子变成p的右孩子。
//右旋类似。static ngx_inline void
ngx_rbtree_left_rotate(ngx_rbtree_node_t **root, ngx_rbtree_node_t *sentinel,ngx_rbtree_node_t *node)   //红黑树的左旋
{ngx_rbtree_node_t  *temp;  //定义一个临时变量temp = node->right;           //获取当前右节点   此时temp就是当前节点的右节点了node->right = temp->left;   ///node的右节点设置为他原来右节点的左节点if (temp->left != sentinel) {   //如果右子结点的左结点不为哨兵temp->left->parent = node;   //右子结点的左子结点挂在左旋结点上}temp->parent = node->parent;  //右节点将会变成原来node的父节点。if (node == *root) {          //是不是根节点的判断*root = temp;} else if (node == node->parent->left) {   //然后把右节点的信息和原来node的parent进行维护node->parent->left = temp;} else {node->parent->right = temp;}temp->left = node;     //现在node变回他原来右节点的子节点了node->parent = temp;   //所以他的parent变成temp
}static ngx_inline void
ngx_rbtree_right_rotate(ngx_rbtree_node_t **root, ngx_rbtree_node_t *sentinel,ngx_rbtree_node_t *node)  //红黑树的右旋
{ngx_rbtree_node_t  *temp;temp = node->left;node->left = temp->right;    //左子结点指向原左子结点的右结点if (temp->right != sentinel) { //如果左子结点的右结点不为哨兵temp->right->parent = node;  //左子结点的右子结点挂在右旋结点上}temp->parent = node->parent;  //左子结点挂在右旋结点的父结点上if (node == *root) { //如果右旋结点为根节点*root = temp;      //根节点赋为左子结点} else if (node == node->parent->right) {    //如果右旋结点为右子结点node->parent->right = temp;             //左子结点挂父结点右边} else {        //否则左子结点挂父结点左边node->parent->left = temp;}temp->right = node;     //现在node变回他原来左节点的子节点了node->parent = temp;   //所以他的parent变成temp
}

gif(太大了,上传不了博主只能上传最次的,要看较好的请点击我的百度网盘进行观看

图片:


博主举得例子也是比较复杂的了,希望大家好好理解,那么相信再去看懂NGINX红黑树部分的源码应该不会在感到无从下手了。

NGINX下红黑树的删除(终章)附GIF相关推荐

  1. 红黑树的删除真的很难吗?其实是你没找到好的解题思路,不信你点击进来看看,建议收藏哦!!!

      上一篇介绍了红黑树的插入操作,这篇来给大家介绍下红黑树的删除操作. 红黑树删除节点   红黑树的节点的删除其实也分为两步: 先删除节点(这步和普通的二叉树删除是一样的) 然后再调整 1.删除节点 ...

  2. 红黑树+java+删除_红黑树深入剖析及Java实现

    红黑树是平衡二叉查找树的一种.为了深入理解红黑树,我们需要从二叉查找树开始讲起. BST 二叉查找树(Binary Search Tree,简称BST)是一棵二叉树,它的左子节点的值比父节点的值要小, ...

  3. 红黑树的删除_Python实现红黑树的删除操作

    上一篇文章使用Python实现了红黑树的插入操作.参考:Python实现红黑树的插入操作本篇文章使用Python实现红黑树的删除操作.先将红黑树的5条特性列出来:1. 节点是红色或黑色.2. 根节点是 ...

  4. 红黑树的删除_红黑树

    红黑树是许多"平衡的"查找树中的一种,它能保证在最坏的情况下,基本的动态集合操作(插入和删除)的时间为O(lgh).我们先简单叙述二叉查找树的性质. 1.1 二叉查找树 二叉查找树 ...

  5. Python实现红黑树的删除操作

    Python实现红黑树的删除操作 本专栏的上一篇文章使用Python实现了红黑树的插入操作.参考:https://blog.csdn.net/weixin_43790276/article/detai ...

  6. 红黑树的删除_红黑树揭秘

    红黑树可能是一般数据结构书中代码逻辑最复杂的结构.多数材料都只是讲构建红黑树的步骤,并没有探究红黑树背后的思想.本文通过对红黑树背后逻辑的梳理,提供给大家一种理解红黑树的方法.首先我们先给出红黑树的定 ...

  7. 红黑树的删除_从红黑树的本质出发,彻底理解红黑树!

    前言 早上好,我是彤哥. 上一节,我们一起从二叉树.二叉查找树.平衡树.AVL树.2-3树.2-3-4树.B树,一路讲到红黑树,最后得出红黑树的本质:红黑树就是2-3-4树,请看下图: 我们知道2-3 ...

  8. 红黑树详解三:红黑树的删除

    系列文章目录 文章目录 系列文章目录 1.删除 2.红黑树的平衡 2.1.N为根节点 2.2.兄弟为黑色节点 2.2.1.兄弟子节点全黑 2.2.1.1.父亲为红色节点 2.2.1.2.父亲为黑色节点 ...

  9. 红黑树的删除操作详解

    红黑树的删除操作 无子节点时,删除节点可能为红色或者黑色: 1.1 如果为红色,直接删除即可,不会影响黑色节点的数量: 1.2 如果为黑色,则需要进行删除平衡的操作了: 只有一个子节点时,删除节点只能 ...

最新文章

  1. No package 'libpcre' found
  2. visio中UML在活动图中指示判定
  3. 临时变量不能作为非const引用
  4. 网站导航颜色停留_做好这几点是建设营销型网站的关键
  5. R语言实战(七)图形进阶
  6. 第十二章 Shell脚本编写及常见面试题(三)
  7. Rafy 框架 - 使用 SqlTree 查询
  8. Python代码优化之in关键字
  9. GO语言实现设计模式【全】
  10. 人工智能与深度学习概念(2)——人工神经网络-ANN
  11. Spring结合马士兵视频的学习经验
  12. 百度文库文档免下载券免费下载方法
  13. word文档在线预览解决方案
  14. 51单片机:定时器/计数器TMOD设定
  15. vue3前端项目引入iconfont阿里图标
  16. javascript三角函数的使用
  17. 我的阴阳两界:革命时期的爱情--王小波
  18. 绿联 蓝牙适配器 linux,绿联USB
  19. python 多元线性回归_多元统计分析之多元线性回归的R语言实现
  20. linux 相机软件,镜像相机app

热门文章

  1. Flutter基础—质感设计
  2. 2021年中国丁香香烟市场趋势报告、技术动态创新及2027年市场预测
  3. 服务 23 年,苹果宣布停止 macOS Server
  4. 新年第一天,3000台Apache服务器宕机
  5. 为什么 Netflix 这么强?网飞 CEO 哈斯廷斯跟陆奇摊牌了
  6. 长沙 · 中国1024程序员节盛况空前,500 万程序员线上线下引爆星城
  7. 优酷用户触达平台技术大揭秘
  8. Git 看这一篇就够了!
  9. 面试官跟我扯了半小时 CountDownLatch 后,给我发 Offer?| 原力计划
  10. 选择什么技术,才能不被淘汰?180 所高校在增设这个专业!