新秀nginx源代码分析数据结构篇(四)红黑树ngx_rbtree_t
新秀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 = 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 插入方法。能够满足一般需求,用户也能够实现自己的插入方法。
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: 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未牵涉到内存池,所以很easy抽出来使用,例如以下为实现了插入、打印最小值、删除的样例
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: }
执行结果:
-
Echo Chen:Blog.csdn.net/chen19870707
-
版权声明:本文博客原创文章,博客,未经同意,不得转载。
新秀nginx源代码分析数据结构篇(四)红黑树ngx_rbtree_t的更多相关文章
- 新秀nginx源代码分析数据结构篇(两) 双链表ngx_queue_t
nginx源代码分析数据结构篇(两) 双链表ngx_queue_t Author:Echo Chen(陈斌) Email:chenb19870707@gmail.com Blog:Blog.csdn. ...
- 菜鸟nginx源代码剖析数据结构篇(一)动态数组ngx_array_t
菜鸟nginx源代码剖析数据结构篇(一)动态数组ngx_array_t Author:Echo Chen(陈斌) Email:chenb19870707@gmail.com Blog:Blog.csd ...
- 菜鸟nginx源代码剖析数据结构篇(九) 内存池ngx_pool_t
菜鸟nginx源代码剖析数据结构篇(九) 内存池ngx_pool_t Author:Echo Chen(陈斌) Email:chenb19870707@gmail.com Blog:Blog.csdn ...
- 菜鸟nginx源代码剖析数据结构篇(八) 缓冲区链表ngx_chain_t
菜鸟nginx源代码剖析数据结构篇(八) 缓冲区链表 ngx_chain_t Author:Echo Chen(陈斌) Email:chenb19870707@gmail.com Blog:Blog. ...
- 菜鸟nginx源代码剖析数据结构篇(十) 自旋锁ngx_spinlock
菜鸟nginx源代码剖析数据结构篇(十) 自旋锁ngx_spinlock Author:Echo Chen(陈斌) Email:chenb19870707@gmail.com Blog:Blog.cs ...
- 菜鸟nginx源代码剖析数据结构篇(六) 哈希表 ngx_hash_t(上)
菜鸟nginx源代码剖析数据结构篇(六) 哈希表 ngx_hash_t(上) Author:Echo Chen(陈斌) Email:chenb19870707@gmail.com Blog:Blog. ...
- 菜鸟nginx源代码剖析数据结构篇(七) 哈希表 ngx_hash_t(下)
菜鸟nginx源代码剖析数据结构篇(七) 哈希表 ngx_hash_t(下) Author:Echo Chen(陈斌) Email:chenb19870707@gmail.com Blog:B ...
- 菜鸟nginx源码剖析数据结构篇(四)红黑树ngx_rbtree_t[转]
菜鸟nginx源码剖析数据结构篇(四)红黑树ngx_rbtree_t Author:Echo Chen(陈斌) Email:chenb19870707@gmail.com Blog:Blog.csdn ...
- nginx源代码分析--进程间通信机制 & 同步机制
Nginx源代码分析-进程间通信机制 从nginx的进程模型能够知道.master进程和worker进程须要通信,nginx中通信的方式有套接字.共享内存.信号.对于master进程,从外部接受信号, ...
随机推荐
- 哈希表之bkdrhash算法解析及扩展
BKDRHASH是一种字符哈希算法,像BKDRHash,APHash.DJBHash,JSHash,RSHash.SDBMHash.PJWHash.ELFHash等等,这些都是比較经典的,通过http ...
- 第三章 AOP 编程选择
Spring为我们开发者提供了多种AOP的编程方式.我们该如何选择呢? 如果项目采用的是JDK5.0以上版本,我们可以选择@AspectJ的方式.这是第一选择. http://blog.csdn.ne ...
- 使用oracle数据库,多用户同时对一个表进行增加,删除,修改,查看等操作,会不会有影响?
使用oracle数据库,多用户同时对一个表进行增加,删除,修改,查看等操作,会不会有影响? 1.问题:各操作间或者性能上会不会有影响? 如果有该如何解决? 多用户操作的影响主要是回锁定记录,oracl ...
- (视频)《高速创建站点》 4.2 完结篇 – 应用运营vs.发射卫星,遥測(Telemetry) 技术
本文是<高速创建站点>系列的第10篇(完结篇),假设你还没有看过之前的内容,建议你点击下面文件夹中的章节先阅读其它内容再回到本文.訪问本系列文件夹.请点击:http://anb.io/bl ...
- (hdu 7.1.8)Quoit Design(最低点——在n一个点,发现两点之间的最小距离)
主题: Quoit Design Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) T ...
- ubuntu 12.04安装经典的Gnome桌面
这次介绍的是如何在ubuntu 12.04 中安装经典的 GNOME桌面,默认的 Ubuntu 12.04 默认unity桌面,一些用户不喜欢 Unity 桌面,所以想找回昔日的经典Gnome桌面. ...
- 牟大哥:《App自我促销》连载2
直立人迁移走
[谋哥每天一干货,第六十九篇] 前篇说到声音在远古时代.是一个奇妙的东西,它可以非常快地把信息传播到其它地方,突破了短距离. 然而能人的后代直立人学会了直立行走,他们開始走出非洲,到达遥远的中东.中国 ...
- ubuntu中KDE与GNOME安装切换
转载:http://apps.hi.baidu.com/share/detail/18919303 1.在Ubuntu中安装KDE桌面命令 sudo apt-get install kUbuntu-d ...
- Oracle集合操作函数:Union、Union All、Intersect、Minus
Union.对两个结果集进行并集操作.不包含反复行,同一时候进行默认规则的排序: Union All.对两个结果集进行并集操作,包含反复行.不进行排序: Intersect,对两个结果集进行交集操作. ...
- OPhone SDK初体验
OPhone SDK初体验 write by 九天雁翎(JTianLing) -- blog.csdn.net/vagrxie 讨论新闻组及文件 背景说明 中国伟大的垄断龙头,世界上也是顶尖的中移动最 ...