nginx中的红黑树

好吧,nginx是个幌子。主要介绍对红黑树的一些理论知识,nginx的主要源码附在后面。这篇文章并不会面面俱到,我的水平也没到那个高度。

红黑树的特性:

1.红黑树是一棵二叉搜索树。

2.树上的每个结点或为红,或为黑。

3.如果一个结点为红色,那么它的左右子结点一定为黑色。

4.从根结点到每个叶子结点路径中经过的黑色结点数是相同的。

5.根结点必须是黑色。

理解这些特性是很重要的。正是因为有这些特性,才保证了红黑树的高效。(至于为什么,我学的还不精,不乱讲了。)。

红黑树的插入和删除结点动作本质上还是按照二叉搜索树的规则来进行的。不过在基本操作完成后,如果违反了红黑树的某个特性,这时才需要做一些旋转操作进行红黑结点的平衡。所以说,红黑树相当一部分的插入和删除动作就是基本的二叉搜索树的插入和删除。本文只列举那些需要旋转处理的情况。

红黑树的插入操作:

约定如下:当前操作结点为X,X的父节点为P,P的父节点PP,P的兄弟结点为S。

根据规则4,新插入的结点(X)必须是red。如果X的父节点(P)也是red。那么违反了规则3,必须通过旋转操作以满足特性。分为三种情况。

I1:

  X为red,P为red,S为black。可以推导出PP也为black。

    

  分为三步:将P置black,将PP置红,PP右旋转。(反向同理)。

I2:

  X为red,P为red,S为black。可以推导出PP也为black。

  

  直接将P左旋转,此时变成了I1。按照I1步骤进行平衡(反向同理,方向相反)。

I3:

  X为red,P为red,S为red。可以推导出PP为black。

  

  这种情况将P和S置black,将PP置red。这种情况比较特殊,如果PP的父节点PPP也为red呢?那么以P结点为新的X向上递归。直到PPP为黑。如果递归到root结点,那么直接设置root为black即可。(相当于每个路径中的black结点数同时+1)。

至此,红黑树的插入操作理论就全部讲完了,我说清了吗?

 红黑树的删除操作: 

  

红黑树的删除操作第一步也遵循二叉搜索树的规律。

我们假设要删除的结点为Y。分为三种情况:

  1. Y为叶子结点,则删除Y结点。Y结点被替换成NULL,记为X。
  2. Y为单支结点,则删除Y结点,用Y的有效子结点(left or right)取代Y的位置。记为X
  3. Y为双支结点,则找到Y结点右子树中的最小结点X(此结点left肯定为NULL)。互换X 和 Y的值(颜色不变)。然后删除X结点(相当于删除了Y结点)。

至此结点的删除已经完成,第二步就是平衡红黑结点,是树满足红黑树的性质。

  1. 如果Y为red,删除red结点不需要平衡操作。
  2. 如果Y为black,X为red。则将X置为black,删除完成。
  3. 如果Y为black,X为black。则需要一些额外的调整以满足红黑树性质。

约定如下:当前操作结点为X,X的父节点为P,X的兄弟结点为S(注意这里是X的兄弟)。S的子结点为Sl Sr。

D1:

  X为black,S为red。则Sl Sr必为black。

  

  变换后并没有使树满足红黑树的性质,而是把它转换为了另一种情况。(D2 D3 D4)。

D2: 

  X为black,S为black。则Sl Sr为black。

  因为删除X,所以X分支比S分支少一个black结点。将S置red后,整个子树左右分支black结点平衡(比其他分支少一个)。所以将P设置为新的X。(这里理解比较容易,不画图了)。

D3:

  X为black,S为black。则Sl 为red Sr位black。

  

  S置red  Sl置black。  S右旋转。转化成D4。

D4:

  X为black,S为black。则Sl 为black Sr位red。

  

  将S设置为P的颜色,P置black。置Sr为black。左旋转P。这样左子树缺失的black结点补回来了。删除完成。

  可见当删除结点有左右子树时,只有D4,才能真正完成平衡。D1 D2 D3都是向D4转化。

  说到后面自己都懵圈了,感觉要出丑。写都写了,还是发出来吧。

  为什么我上面的理论说的这么简单粗暴呢?nginx就是这么写的呀。

插入代码:

void
ngx_rbtree_insert(ngx_rbtree_t *tree, ngx_rbtree_node_t *node)
{
    ngx_rbtree_node_t  **root, *temp, *sentinel;

    /* a binary tree insert */

    root = (ngx_rbtree_node_t **) &tree->root;
    sentinel = tree->sentinel;   //sentinel为哨兵,我觉得就是NULL

    if (*root == sentinel) {
        node->parent = NULL;
        node->left = sentinel;
        node->right = sentinel;
        ngx_rbt_black(node);
        *root = node;

        return;
    }
    tree->insert(*root, node, sentinel);   //按照二叉搜索树插入结点

    /* re-balance tree */

    while (node != *root && ngx_rbt_is_red(node->parent)) { //平衡红黑结点

        if (node->parent == node->parent->parent->left) {
            temp = node->parent->parent->right;

            if (ngx_rbt_is_red(temp)) {      //I3
                ngx_rbt_black(node->parent);
                ngx_rbt_black(temp);
                ngx_rbt_red(node->parent->parent);
                node = node->parent->parent;

            } else {
                if (node == node->parent->right) {    //I2
                    node = node->parent;
                    ngx_rbtree_left_rotate(root, sentinel, node);
                }

                ngx_rbt_black(node->parent);         //I1
                ngx_rbt_red(node->parent->parent);
                ngx_rbtree_right_rotate(root, sentinel, node->parent->parent);
            }

        } else {                        //反向同理,方向相反。
            temp = node->parent->parent->left;

            if (ngx_rbt_is_red(temp)) {
                ngx_rbt_black(node->parent);
                ngx_rbt_black(temp);
                ngx_rbt_red(node->parent->parent);
                node = node->parent->parent;

            } else {
                if (node == node->parent->left) {
                    node = node->parent;
                    ngx_rbtree_right_rotate(root, sentinel, node);
                }

                ngx_rbt_black(node->parent);
                ngx_rbt_red(node->parent->parent);
                ngx_rbtree_left_rotate(root, sentinel, node->parent->parent);
            }
        }
    }

    ngx_rbt_black(*root);    //无脑设置,任何情况都对。
}

删除代码:

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 = (ngx_rbtree_node_t **) &tree->root;
    sentinel = tree->sentinel;   //哨兵(NULL)

    if (node->left == sentinel) {//情况1 2  删除结点Y为单支结点或者叶子结点
        temp = node->right;
        subst = node;

    } else if (node->right == sentinel) {//情况1 2  删除结点Y为单支结点或者叶子结点
        temp = node->left;
        subst = node;

    } else { //情况1 情况3   删除结点Y为双支结点
        subst = ngx_rbtree_min(node->right, sentinel);

        if (subst->left != sentinel) {
            temp = subst->left;
        } else {
            temp = subst->right;
        }
    }

    if (subst == *root) {
        *root = temp;
        ngx_rbt_black(temp);

        /* DEBUG stuff */
        node->left = NULL;
        node->right = NULL;
        node->parent = NULL;
        node->key = 0;

        return;
    }

    red = ngx_rbt_is_red(subst);  //记录Y的颜色

    if (subst == subst->parent->left) {  //这里是按照二叉搜索树的性质,删除结点后重新生成二叉搜索树。
        subst->parent->left = temp;

    } else {
        subst->parent->right = temp;
    }

    if (subst == node) {

        temp->parent = subst->parent;

    } else {

        if (subst->parent == node) {
            temp->parent = subst;

        } else {
            temp->parent = subst->parent;
        }

        subst->left = node->left;
        subst->right = node->right;
        subst->parent = node->parent;
        ngx_rbt_copy_color(subst, node);

        if (node == *root) {
            *root = subst;

        } else {
            if (node == node->parent->left) {
                node->parent->left = subst;
            } else {
                node->parent->right = subst;
            }
        }

        if (subst->left != sentinel) {
            subst->left->parent = subst;
        }

        if (subst->right != sentinel) {
            subst->right->parent = subst;
        }
    }

    /* DEBUG stuff */
    node->left = NULL;
    node->right = NULL;
    node->parent = NULL;
    node->key = 0;

    if (red) {                     //删除结点Y为red  直接退出
        return;
    }

    /* a delete fixup */

    while (temp != *root && ngx_rbt_is_black(temp)) {  //temp就是上文中的X。开始平衡红黑结点。直到X为红色,或者到达root

        if (temp == temp->parent->left) {
            w = temp->parent->right;

            if (ngx_rbt_is_red(w)) {              //D1
                ngx_rbt_black(w);
                ngx_rbt_red(temp->parent);
                ngx_rbtree_left_rotate(root, sentinel, temp->parent);
                w = temp->parent->right;
            }

            if (ngx_rbt_is_black(w->left) && ngx_rbt_is_black(w->right)) {//D2
                ngx_rbt_red(w);
                temp = temp->parent;

            } else {
                if (ngx_rbt_is_black(w->right)) {    //D3
                    ngx_rbt_black(w->left);
                    ngx_rbt_red(w);
                    ngx_rbtree_right_rotate(root, sentinel, w);
                    w = temp->parent->right;
                }

                ngx_rbt_copy_color(w, temp->parent);  //D4
                ngx_rbt_black(temp->parent);
                ngx_rbt_black(w->right);
                ngx_rbtree_left_rotate(root, sentinel, temp->parent);
                temp = *root;
            }

        } else {                           //反向同理,方向相反。
            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);   //置black
}

我这写的真是shit啊。研究nginx源码的凑合着看吧。红黑树研究透了卷土重写。轻点吐槽。

  

时间: 2024-10-28 20:50:05

nginx中的红黑树的相关文章

Java集合详解6:这次,从头到尾带你解读Java中的红黑树

<Java集合详解系列>是我在完成夯实Java基础篇的系列博客后准备开始写的新系列. 这些文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查看 https://github.com/h2pl/Java-Tutorial 喜欢的话麻烦点下Star.fork哈 文章首发于我的个人博客: www.how2playlife.com 什么是红黑树 首先,什么是红黑树呢? 红黑树是一种"平衡的"二叉查找树,它是一种经典高效的算法,能够保证

nginx学习九 高级数据结构之红黑树ngx_rbtree_t

1红黑树简介 先来看下算法导论对R-B Tree的介绍: 红黑树,一种二叉查找树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或Black. 通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其他路径长出俩倍,因而是接近平的. 红黑树,作为一棵二叉查找树,满足二叉查找树的一般性质.下面,来了解下 二叉查找树的一般性质. 二叉查找树 二叉查找树,也称有序二叉树(ordered binary tree),或已排序二叉树(sorted binary tree

java中treemap和treeset实现(红黑树)

TreeMap 的实现就是红黑树数据结构,也就说是一棵自平衡的排序二叉树,这样就可以保证当需要快速检索指定节点. TreeSet 和 TreeMap 的关系 为了让大家了解 TreeMap 和 TreeSet 之间的关系,下面先看 TreeSet 类的部分源代码: public class TreeSet<E> extends AbstractSet<E> implements NavigableSet<E>, Cloneable, java.io.Serializab

简单聊聊红黑树(Red Black Tree)

? 前言 众所周知,红黑树是非常经典,也很非常重要的数据结构,自从1972年被发明以来,因为其稳定高效的特性,40多年的时间里,红黑树一直应用在许多系统组件和基础类库中,默默无闻的为我们提供服务,身边有很多同学经常问红黑树是怎么实现的,所以在这里想写一篇文章简单和大家聊聊下红黑树 小编看过很多讲红黑树的文章,都不是很容易懂,主要也是因为完整的红黑树很复杂,想通过一篇文章来说清楚实在很难,所以在这篇文章中我想尽量用通俗口语化的语言,再结合 Robert Sedgewick 在<算法>中的改进的版

数据结构-红黑树

转自:http://dongxicheng.org/structure/red-black-tree/ 1. 简介 红黑树是一种自平衡二叉查找树.它的统计性能要好于平衡二叉树(AVL树),因此,红黑树在很多地方都有应用.在C++ STL中,很多部分(目前包括set, multiset, map, multimap)应用了红黑树的变体(SGI STL中的红黑树有一些变化,这些修改提供了更好的性能,以及对set操作的支持).它是复杂的,但它的操作有着良好的最坏情况运行时间,并且在实践中是高效的: 它

红黑树(一)之 原理和算法详细介绍---转帖

目录1 红黑树的介绍2 红黑树的应用3 红黑树的时间复杂度和相关证明4 红黑树的基本操作(一) 左旋和右旋5 红黑树的基本操作(二) 添加6 红黑树的基本操作(三) 删除 作者:Sky Wang    于 2013-08-08 概述:R-B Tree,又称为"红黑树".本文参考了<算法导论>中红黑树相关知识,加之自己的理解,然后以图文的形式对红黑树进行说明.本文的主要内容包括:红黑树的特性,红黑树的时间复杂度和它的证明,红黑树的左旋.右旋.插入.删除等操作. 请尊重版权,转

jdk源码分析红黑树——插入篇

红黑树是自平衡的排序树,自平衡的优点是减少遍历的节点,所以效率会高.如果是非平衡的二叉树,当顺序或逆序插入的时候,查找动作很可能会遍历n个节点 红黑树的规则很容易理解,但是维护这个规则难. 一.规则 1.每个节点要么是红色.要么是黑色 2.根节点一定是黑色 3.红色节点不可以连续出现(父节点.子节点不可同时为红) 4.从任意节点出发,到树底的所有路线,途径的黑节点数量必须相同 在修改红黑树的时候,切记要维护这个规则.一般默认插入红色节点(除非是root节点),插入后再进行旋转和颜色变换 二.旋转

红黑树(一)之原理和算法的详细分析【转】

本文转载自:http://www.cnblogs.com/skywang12345/p/3245399.html R-B Tree简介 R-B Tree,全称是Red-Black Tree,又称为“红黑树”,它一种特殊的二叉查找树.红黑树的每个节点上都有存储位表示节点的颜色,可以是红(Red)或黑(Black). 红黑树的特性:(1)每个节点或者是黑色,或者是红色.(2)根节点是黑色.(3)每个叶子节点(NIL)是黑色. [注意:这里叶子节点,是指为空(NIL或NULL)的叶子节点!](4)如果

数据结构--树--红黑树

R-B Tree简介 R-B Tree,全称是Red-Black Tree,又称为“红黑树”,它一种特殊的二叉查找树.红黑树的每个节点上都有存储位表示节点的颜色,可以是红(Red)或黑(Black). 红黑树的特性:(1)每个节点或者是黑色,或者是红色.(2)根节点是黑色.(3)每个叶子节点(NIL)是黑色. [注意:这里叶子节点,是指为空(NIL或NULL)的叶子节点!](4)如果一个节点是红色的,则它的子节点必须是黑色的.(5)从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点.