NGINX下红黑树的删除(终章)附GIF(2)

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

NGINX下红黑树的删除(终章)附GIF(1):https://developer.aliyun.com/article/1414760

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

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;       //树根指针的指针赋给了root
    sentinel = 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(太大了,上传不了博主只能上传最次的,要看较好的请点击我的百度网盘进行观看


image.gif


图片:

20191105181933147.png

20191105181942570.png

20191105181955365.png

20191105182008602.png

       20191105182024199.png                        

20191105182031458.png

20191105182035425.png

20191105182037937.png


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

目录
相关文章
|
6月前
|
算法 应用服务中间件 nginx
NGINX下的红黑树源码详解(附 流程图和GIF)(1)
之前博主稍微讲解了下红黑树的原理,那么在这篇博客博主想要把红黑树讲的更加的透彻,以便于更多的人了解红黑树 (本博客会更加详细的介绍之前的博客没介绍到的,所以各位看官不同再回去翻看博主之前那篇红黑树的原理讲解了。)
105 3
|
域名解析 负载均衡 网络协议
Nginx系列教程(完) -终章总结
Nginx系列教程(完) -终章总结
68 0
|
6月前
|
算法 应用服务中间件 nginx
NGINX下的红黑树源码详解(附 流程图和GIF)(2)
那我们就接着之前的gif继续吧 涉及到的 3/4、5情况(精简版) 情况3:变化前[当前结点为4节点]: 当前节点的父节点是红色且祖父节点的另一个子节点(叔叔节点)是红色。
57 2
|
6月前
|
应用服务中间件 nginx
NGINX下红黑树的删除(终章)附GIF(1)
接着上一篇我们就只剩下了红黑树的删除了,这也是较为复杂的操作(原理一套gif(只是简单部分),代码两套gif(困难部分博主会从头讲到尾)),因为删除操作比较复杂,所以博主打算简单一套,复杂一套,希望大家看了博主的博客以后不要在惧怕红黑树了!!!
40 0
|
存储 缓存 监控
Nginx:epoll红黑树和双向链表究竟如何做到少量拷贝和轮循实现高并发的
Nginx:epoll红黑树和双向链表究竟如何做到少量拷贝和轮循实现高并发的
|
1月前
|
应用服务中间件 BI nginx
Nginx的location配置详解
【10月更文挑战第16天】Nginx的location配置详解
|
1月前
|
缓存 负载均衡 安全
Nginx常用基本配置总结:从入门到实战的全方位指南
Nginx常用基本配置总结:从入门到实战的全方位指南
262 0
|
1月前
|
应用服务中间件 Linux nginx
Jetson 环境安装(四):jetson nano配置ffmpeg和nginx(亲测)之编译错误汇总
这篇文章是关于在Jetson Nano上配置FFmpeg和Nginx时遇到的编译错误及其解决方案的汇总。
93 4
|
10天前
|
存储 负载均衡 中间件
Nginx反向代理配置详解,图文全面总结,建议收藏
Nginx 是大型架构必备中间件,也是大厂喜欢考察的内容,必知必会。本篇全面详解 Nginx 反向代理及配置,建议收藏。
Nginx反向代理配置详解,图文全面总结,建议收藏
|
23天前
|
应用服务中间件 API nginx
nginx配置反向代理404问题
【10月更文挑战第18天】本文介绍了使用Nginx进行反向代理的配置方法,解决了404错误、跨域问题和302重定向问题。关键配置包括代理路径、请求头设置、跨域头添加以及端口转发设置。通过调整`proxy_set_header`和添加必要的HTTP头,实现了稳定的服务代理和跨域访问。
116 1
nginx配置反向代理404问题