菜鸟nginx源码剖析数据结构篇(四)红黑树ngx_rbtree_t
-
Author:Echo Chen(陈斌)
-
Email:chenb19870707@gmail.com
-
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 = i6: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 void2: 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 void2: 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 插入方法,可以满足一般需求,用户也可以实现自己的插入方法。
- void
- ngx_rbtree_insert(ngx_thread_volatile 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;
- 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)) {
- 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) {
- node = node->parent;
- ngx_rbtree_left_rotate(root, sentinel, node);
- }
- ngx_rbt_black(node->parent);
- 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);
- }
6.1 唯一值类型插入
这个即为一般红黑树的插入方法,循环,如果插入的值比当前节点小,就进入左子树,否则进入右子树,直至遇到叶子结点,叶子节点就是要链入红黑树的位置。
1: void2: 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: void3: 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 values12: * 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: void2: 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: }运行结果:
-
浙公网安备 33010602011771号