zoukankan      html  css  js  c++  java
  • 菜鸟nginx源码剖析数据结构篇(四)红黑树ngx_rbtree_t[转]

    菜鸟nginx源码剖析数据结构篇(四)红黑树ngx_rbtree_t

    • Author:Echo Chen(陈斌)

    • Email:chenb19870707@gmail.com

    • Blog:Blog.csdn.net/chen19870707

    • Date:October 27h, 2014

       

      1.ngx_rbtree优势和特点

          ngx_rbtree是一种使用红黑树实现的关联容器,关于红黑树的特性,在《手把手实现红黑树》已经详细介绍,这里就只探讨ngx_rbtree与众不同的地方;ngx_rbtree红黑树容器中的元素都是有序的,支持快速索引,插入,删除操作,也支持范围查询,遍历操作,应用非常广泛。

      2.源代码位置

      头文件:http://trac.nginx.org/nginx/browser/nginx/src/core/ngx_rbtree.h

      源文件:http://trac.nginx.org/nginx/browser/nginx/src/core/ngx_rbtree.c

      3.数据结构定义

      可以看到ngx_rbtree的结点ngx_rbtree_node_t结构跟一般的红黑树差不多,都是由键值key、左孩子left、右孩子right、父亲结点parent、颜色值color,不同的是ngx_rbtree_node_t这里多了一个data,但根据官方文档记在,由于data只有一个字节,表示太少,很少使用到

         1: typedef struct ngx_rbtree_node_s  ngx_rbtree_node_t;
         2:  
         3: struct ngx_rbtree_node_s {
         4:     ngx_rbtree_key_t       key;
         5:     ngx_rbtree_node_t     *left;
         6:     ngx_rbtree_node_t     *right;
         7:     ngx_rbtree_node_t     *parent;
         8:     u_char                 color;
         9:     u_char                 data;
        10: };

      ngx_rbtree_t的结构也与一般红黑树相同,右root结点和哨兵叶子结点(sentinel)组成,不同的是这里多了一个 函数指针inserter,它决定了在添加结点是新加还是替换。

         1: typedef struct ngx_rbtree_s  ngx_rbtree_t;
         2:  
         3: typedef void (*ngx_rbtree_insert_pt) (ngx_rbtree_node_t *root,
         4:     ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);
         5:  
         6: struct ngx_rbtree_s {
         7:     ngx_rbtree_node_t     *root;
         8:     ngx_rbtree_node_t     *sentinel;
         9:     ngx_rbtree_insert_pt   insert;
        10: };

      4.ngx_rbtree初始化 ngx_rbtree_init

       

      其中tree为ngx_rbtree_t类型,即为红黑树,s为ngx_rbtree_node_t,是rbtree的根节点,i即为上节提到的决定插入是新结点还是替换的函数指针。首先将根节点涂成 黑色(红黑树基本性质),然后把 红黑树的 根节点和 哨兵结点 都指向这个结点。

         1: #define ngx_rbtree_init(tree, s, i)                                           
         2:     ngx_rbtree_sentinel_init(s);                                              
         3:     (tree)->root = s;                                                         
         4:     (tree)->sentinel = s;                                                     
         5:     (tree)->insert = i
         6:  
         7: #define ngx_rbtree_sentinel_init(node)  ngx_rbt_black(node)

      5.ngx_rbtree 左旋 ngx_rbtree_left_rotate 和 右旋 ngx_rbtree_right_rotate

      可以看到,经典代码总是永恒的,ngx_rbtree的左旋右旋也是参考《算法导论》导论中的步骤和伪代码,对照我自己的实现的《手把手实现红黑树》,与我自己实现的左旋右旋代码基本一致,我图解了详细的过程,有不清楚的可以参考《手把手实现红黑树》。

         1: static ngx_inline void
         2: ngx_rbtree_left_rotate(ngx_rbtree_node_t **root, ngx_rbtree_node_t *sentinel,
         3:     ngx_rbtree_node_t *node)
         4: {
         5:     ngx_rbtree_node_t  *temp;
         6:  
         7:     temp = node->right;
         8:     node->right = temp->left;
         9:  
        10:     if (temp->left != sentinel) {
        11:         temp->left->parent = node;
        12:     }
        13:  
        14:     temp->parent = node->parent;
        15:  
        16:     if (node == *root) {
        17:         *root = temp;
        18:  
        19:     } else if (node == node->parent->left) {
        20:         node->parent->left = temp;
        21:  
        22:     } else {
        23:         node->parent->right = temp;
        24:     }
        25:  
        26:     temp->left = node;
        27:     node->parent = temp;
        28: }
         1: static ngx_inline void
         2: ngx_rbtree_right_rotate(ngx_rbtree_node_t **root, ngx_rbtree_node_t *sentinel,
         3:     ngx_rbtree_node_t *node)
         4: {
         5:     ngx_rbtree_node_t  *temp;
         6:  
         7:     temp = node->left;
         8:     node->left = temp->right;
         9:  
        10:     if (temp->right != sentinel) {
        11:         temp->right->parent = node;
        12:     }
        13:  
        14:     temp->parent = node->parent;
        15:  
        16:     if (node == *root) {
        17:         *root = temp;
        18:  
        19:     } else if (node == node->parent->right) {
        20:         node->parent->right = temp;
        21:  
        22:     } else {
        23:         node->parent->left = temp;
        24:     }
        25:  
        26:     temp->right = node;
        27:     node->parent = temp;
        28: }

      6.ngx_rbtree插入 ngx_rbtree_insert

      ngx_rbtree_insert也是分为两步,插入和调整,由于这两项都在《手把手实现红黑树》中做了详细解释,这里就不在啰嗦,这里值得一提的是,还记得node_rbtree_t 结构中的insert指针吗?这里就是通过这个函数指针来实现的插入。一个小小的技巧就实现了多态;并且它给出了 唯一值和时间类型的key 插入方法,可以满足一般需求,用户也可以实现自己的插入方法。

      1. void  
      2. ngx_rbtree_insert(ngx_thread_volatile ngx_rbtree_t *tree,  
      3.     ngx_rbtree_node_t *node)  
      4. {  
      5.     ngx_rbtree_node_t  **root, *temp, *sentinel;  
      6.   
      7.     /* a binary tree insert */  
      8.   
      9.     root = (ngx_rbtree_node_t **) &tree->root;  
      10.     sentinel = tree->sentinel;  
      11.   
      12.     if (*root == sentinel) {  
      13.         node->parent = NULL;  
      14.         node->left = sentinel;  
      15.         node->right = sentinel;  
      16.         ngx_rbt_black(node);  
      17.         *root = node;  
      18.   
      19.         return;  
      20.     }  
      21.   
      22.     tree->insert(*root, node, sentinel);  
      23.   
      24.     /* re-balance tree */  
      25.   
      26.     while (node != *root && ngx_rbt_is_red(node->parent)) {  
      27.   
      28.         if (node->parent == node->parent->parent->left) {  
      29.             temp = node->parent->parent->right;  
      30.   
      31.             if (ngx_rbt_is_red(temp)) {  
      32.                 ngx_rbt_black(node->parent);  
      33.                 ngx_rbt_black(temp);  
      34.                 ngx_rbt_red(node->parent->parent);  
      35.                 node = node->parent->parent;  
      36.   
      37.             } else {  
      38.                 if (node == node->parent->right) {  
      39.                     node = node->parent;  
      40.                     ngx_rbtree_left_rotate(root, sentinel, node);  
      41.                 }  
      42.   
      43.                 ngx_rbt_black(node->parent);  
      44.                 ngx_rbt_red(node->parent->parent);  
      45.                 ngx_rbtree_right_rotate(root, sentinel, node->parent->parent);  
      46.             }  
      47.   
      48.         } else {  
      49.             temp = node->parent->parent->left;  
      50.   
      51.             if (ngx_rbt_is_red(temp)) {  
      52.                 ngx_rbt_black(node->parent);  
      53.                 ngx_rbt_black(temp);  
      54.                 ngx_rbt_red(node->parent->parent);  
      55.                 node = node->parent->parent;  
      56.   
      57.             } else {  
      58.                 if (node == node->parent->left) {  
      59.                     node = node->parent;  
      60.                     ngx_rbtree_right_rotate(root, sentinel, node);  
      61.                 }  
      62.   
      63.                 ngx_rbt_black(node->parent);  
      64.                 ngx_rbt_red(node->parent->parent);  
      65.                 ngx_rbtree_left_rotate(root, sentinel, node->parent->parent);  
      66.             }  
      67.         }  
      68.     }  
      69.   
      70.     ngx_rbt_black(*root);  
      71. }  

      6.1 唯一值类型插入

      这个即为一般红黑树的插入方法,循环,如果插入的值比当前节点小,就进入左子树,否则进入右子树,直至遇到叶子结点,叶子节点就是要链入红黑树的位置。

         1: void
         2: ngx_rbtree_insert_value(ngx_rbtree_node_t *temp, ngx_rbtree_node_t *node,
         3:     ngx_rbtree_node_t *sentinel)
         4: {
         5:     ngx_rbtree_node_t  **p;
         6:  
         7:     for ( ;; ) {
         8:  
         9:         p = (node->key < temp->key) ? &temp->left : &temp->right;
        10:  
        11:         if (*p == sentinel) {
        12:             break;
        13:         }
        14:  
        15:         temp = *p;
        16:     }
        17:  
        18:     *p = node;
        19:     node->parent = temp;
        20:     node->left = sentinel;
        21:     node->right = sentinel;
        22:     ngx_rbt_red(node);
        23: }

      如果有相等的结点,会直接被覆盖,如上图插入key为2的结点,则当tmp 为2的结点时,p为叶子遍历结束,这样p就会被覆盖为新的值。

      6.2 唯一时间类型插入

      唯一区别就是判断大小时,采用了两个值相减,避免溢出。

         1: typedef ngx_int_t   ngx_rbtree_key_int_t;
         2: void
         3: ngx_rbtree_insert_timer_value(ngx_rbtree_node_t *temp, ngx_rbtree_node_t *node,
         4:     ngx_rbtree_node_t *sentinel)
         5: {
         6:     ngx_rbtree_node_t  **p;
         7:  
         8:     for ( ;; ) {
         9:  
        10:         /*
        11:          * Timer values
        12:          * 1) are spread in small range, usually several minutes,
        13:          * 2) and overflow each 49 days, if milliseconds are stored in 32 bits.
        14:          * The comparison takes into account that overflow.
        15:          */
        16:  
        17:         /*  node->key < temp->key */
        18:  
        19:         p = ((ngx_rbtree_key_int_t) (node->key - temp->key) < 0)
        20:             ? &temp->left : &temp->right;
        21:  
        22:         if (*p == sentinel) {
        23:             break;
        24:         }
        25:  
        26:         temp = *p;
        27:     }
        28:  
        29:     *p = node;
        30:     node->parent = temp;
        31:     node->left = sentinel;
        32:     node->right = sentinel;
        33:     ngx_rbt_red(node);
        34: }

      7.ngx_rbtree删除ngx_rbtree_delete

      也是按照《算法导论》上的步骤,先删除后调整,在《手把手实现红黑树》已介绍,请参考

         1: void
         2: ngx_rbtree_delete_delete(ngx_thread_volatile ngx_rbtree_t *tree,
         3:     ngx_rbtree_node_t *node)
         4: {
         5:     ngx_uint_t           red;
         6:     ngx_rbtree_node_t  **root, *sentinel, *subst, *temp, *w;
         7:  
         8:     /* a binary tree delete */
         9:  
        10:     root = (ngx_rbtree_node_t **) &tree->root;
        11:     sentinel = tree->sentinel;
        12:  
        13:     if (node->left == sentinel) {
        14:         temp = node->right;
        15:         subst = node;
        16:  
        17:     } else if (node->right == sentinel) {
        18:         temp = node->left;
        19:         subst = node;
        20:  
        21:     } else {
        22:         subst = ngx_rbtree_min(node->right, sentinel);
        23:  
        24:         if (subst->left != sentinel) {
        25:             temp = subst->left;
        26:         } else {
        27:             temp = subst->right;
        28:         }
        29:     }
        30:  
        31:     if (subst == *root) {
        32:         *root = temp;
        33:         ngx_rbt_black(temp);
        34:  
        35:         /* DEBUG stuff */
        36:         node->left = NULL;
        37:         node->right = NULL;
        38:         node->parent = NULL;
        39:         node->key = 0;
        40:  
        41:         return;
        42:     }
        43:  
        44:     red = ngx_rbt_is_red(subst);
        45:  
        46:     if (subst == subst->parent->left) {
        47:         subst->parent->left = temp;
        48:  
        49:     } else {
        50:         subst->parent->right = temp;
        51:     }
        52:  
        53:     if (subst == node) {
        54:  
        55:         temp->parent = subst->parent;
        56:  
        57:     } else {
        58:  
        59:         if (subst->parent == node) {
        60:             temp->parent = subst;
        61:  
        62:         } else {
        63:             temp->parent = subst->parent;
        64:         }
        65:  
        66:         subst->left = node->left;
        67:         subst->right = node->right;
        68:         subst->parent = node->parent;
        69:         ngx_rbt_copy_color(subst, node);
        70:  
        71:         if (node == *root) {
        72:             *root = subst;
        73:  
        74:         } else {
        75:             if (node == node->parent->left) {
        76:                 node->parent->left = subst;
        77:             } else {
        78:                 node->parent->right = subst;
        79:             }
        80:         }
        81:  
        82:         if (subst->left != sentinel) {
        83:             subst->left->parent = subst;
        84:         }
        85:  
        86:         if (subst->right != sentinel) {
        87:             subst->right->parent = subst;
        88:         }
        89:     }
        90:  
        91:     /* DEBUG stuff */
        92:     node->left = NULL;
        93:     node->right = NULL;
        94:     node->parent = NULL;
        95:     node->key = 0;
        96:  
        97:     if (red) {
        98:         return;
        99:     }
       100:  
       101:     /* a delete fixup */
       102:  
       103:     while (temp != *root && ngx_rbt_is_black(temp)) {
       104:  
       105:         if (temp == temp->parent->left) {
       106:             w = temp->parent->right;
       107:  
       108:             if (ngx_rbt_is_red(w)) {
       109:                 ngx_rbt_black(w);
       110:                 ngx_rbt_red(temp->parent);
       111:                 ngx_rbtree_left_rotate(root, sentinel, temp->parent);
       112:                 w = temp->parent->right;
       113:             }
       114:  
       115:             if (ngx_rbt_is_black(w->left) && ngx_rbt_is_black(w->right)) {
       116:                 ngx_rbt_red(w);
       117:                 temp = temp->parent;
       118:  
       119:             } else {
       120:                 if (ngx_rbt_is_black(w->right)) {
       121:                     ngx_rbt_black(w->left);
       122:                     ngx_rbt_red(w);
       123:                     ngx_rbtree_right_rotate(root, sentinel, w);
       124:                     w = temp->parent->right;
       125:                 }
       126:  
       127:                 ngx_rbt_copy_color(w, temp->parent);
       128:                 ngx_rbt_black(temp->parent);
       129:                 ngx_rbt_black(w->right);
       130:                 ngx_rbtree_left_rotate(root, sentinel, temp->parent);
       131:                 temp = *root;
       132:             }
       133:  
       134:         } else {
       135:             w = temp->parent->left;
       136:  
       137:             if (ngx_rbt_is_red(w)) {
       138:                 ngx_rbt_black(w);
       139:                 ngx_rbt_red(temp->parent);
       140:                 ngx_rbtree_right_rotate(root, sentinel, temp->parent);
       141:                 w = temp->parent->left;
       142:             }
       143:  
       144:             if (ngx_rbt_is_black(w->left) && ngx_rbt_is_black(w->right)) {
       145:                 ngx_rbt_red(w);
       146:                 temp = temp->parent;
       147:  
       148:             } else {
       149:                 if (ngx_rbt_is_black(w->left)) {
       150:                     ngx_rbt_black(w->right);
       151:                     ngx_rbt_red(w);
       152:                     ngx_rbtree_left_rotate(root, sentinel, w);
       153:                     w = temp->parent->left;
       154:                 }
       155:  
       156:                 ngx_rbt_copy_color(w, temp->parent);
       157:                 ngx_rbt_black(temp->parent);
       158:                 ngx_rbt_black(w->left);
       159:                 ngx_rbtree_right_rotate(root, sentinel, temp->parent);
       160:                 temp = *root;
       161:             }
       162:         }
       163:     }
       164:  
       165:     ngx_rbt_black(temp);
       166: }

      8.实战

      由于ngx_rbtree_t未牵涉到内存池,所以非常容易抽出来使用,如下为实现了插入、打印最小值、删除的例子

         1: #include <iostream>
         2: #include <algorithm>
         3: #include <pthread.h>
         4: #include <time.h>
         5: #include <stdio.h>
         6: #include <errno.h>
         7: #include <string.h>
         8: #include "ngx_queue.h"
         9: #include "ngx_rbtree.h"
        10:  
        11:  
        12: int main()
        13: {
        14:  
        15:     ngx_rbtree_t tree;
        16:     ngx_rbtree_node_t sentinel;
        17:  
        18:     ngx_rbtree_init(&tree,&sentinel,ngx_rbtree_insert_value);
        19:  
        20:     ngx_rbtree_node_t *rbnode = new ngx_rbtree_node_t[100];
        21:     for(int i = 99; i >= 0 ;i--)
        22:     {
        23:         rbnode[i].key = i;
        24:         rbnode[i].parent = NULL;
        25:         rbnode[i].left = NULL;
        26:         rbnode[i].right = NULL;
        27:         ngx_rbtree_insert(&tree,&rbnode[i]);
        28:     }
        29:  
        30:     for(int i = 0; i < 100;i++)
        31:     {
        32:          ngx_rbtree_node_t *p = ngx_rbtree_min(tree.root,&sentinel);
        33:          std::cout << p->key << "  ";
        34:          ngx_rbtree_delete(&tree,p);
        35:      }
        36:  
        37:  
        38:     delete[] rbnode;
        39:  
        40:     return 0;
        41: }

      运行结果:

      -

  • 相关阅读:
    uboot学习之三-----uboot启动第一阶段--start.S之一
    4.7 C语言的存储类,作用域,生命周期,链接属性
    uboot学习之二----主Makefile学习之一----版本号 u_boot_version(U_BOOT_VERSION = $(VERSION).$(PATCHLEVEL).$(SUBLEVEL).$(EXTRAVERSION)) (24-29行)
    uboot学习之二----主Makefile学习之二----环境变量之:主机的操作系统和主机架构(HOSTOS、HOSTARCH)(31-43行)
    uboot学习之二----主Makefile学习之三----静默编译
    uboot学习之二----主Makefile学习之四----两种编译方法:原地编译和单独输出文件夹编译
    Linux下安装交叉工具链&&安装vim
    善变的不只是女人,还有volatile ---偷来的标题名
    东芝半导体最新ARM开发板——TT_M3HQ开箱评测
    STM32F407外部晶体改为25M后检测不到芯片的解决办法
  • 原文地址:https://www.cnblogs.com/0x2D-0x22/p/4139789.html
Copyright © 2011-2022 走看看