新秀nginx源代码分析数据结构篇(四)红黑树ngx_rbtree_t
?
新秀nginx源代碼分析數(shù)據(jù)結(jié)構(gòu)篇(四)紅黑樹ngx_rbtree_t
?
Author:Echo Chen(陳斌)
Email:chenb19870707@gmail.com
Blog:Blog.csdn.net/chen19870707
Date:October 27h, 2014
?
?
1.ngx_rbtree優(yōu)勢和特點
?
??? ngx_rbtree是一種使用紅黑樹實現(xiàn)的關(guān)聯(lián)容器。關(guān)于紅黑樹的特性,在《手把手實現(xiàn)紅黑樹》已經(jīng)具體介紹,這里就僅僅探討ngx_rbtree與眾不同的地方;ngx_rbtree紅黑樹容器中的元素都是有序的,支持高速索引,插入,刪除操作,也支持范圍查詢,遍歷操作。應(yīng)用很廣泛。
?
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.數(shù)據(jù)結(jié)構(gòu)定義
?
能夠看到ngx_rbtree的結(jié)點ngx_rbtree_node_t結(jié)構(gòu)跟一般的紅黑樹差點兒相同,都是由鍵值key、左孩子left、右孩子right、父親結(jié)點parent、顏色值color,不同的是ngx_rbtree_node_t這里多了一個data。但依據(jù)官方文檔記在,因為data僅僅有一個字節(jié),表示太少,非常少使用到。
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的結(jié)構(gòu)也與一般紅黑樹同樣,右root結(jié)點和哨兵葉子結(jié)點(sentinel)組成,不同的是這里多了一個 函數(shù)指針inserter。它決定了在加入結(jié)點是新加還是替換。
?
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
?
當(dāng)中tree為ngx_rbtree_t類型,即為紅黑樹。s為ngx_rbtree_node_t,是rbtree的根節(jié)點,i即為上節(jié)提到的決定插入是新結(jié)點還是替換的函數(shù)指針。首先將根節(jié)點涂成 黑色(紅黑樹基本性質(zhì))。然后把 紅黑樹的 根節(jié)點和 哨兵結(jié)點 都指向這個結(jié)點。
?
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
?
能夠看到,經(jīng)典代碼總是永恒的,ngx_rbtree的左旋右旋也是參考《算法導(dǎo)論》導(dǎo)論中的步驟和偽代碼。對比我自己的實現(xiàn)的《手把手實現(xiàn)紅黑樹》,與我自己實現(xiàn)的左旋右旋代碼基本一致。我圖解了具體的過程,有不清楚的能夠參考《手把手實現(xiàn)紅黑樹》。
?
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也是分為兩步,插入和調(diào)整。因為這兩項都在《手把手實現(xiàn)紅黑樹》中做了詳解,這里就不在啰嗦。這里值得一提的是,還記得node_rbtree_t 結(jié)構(gòu)中的insert指針嗎?這里就是通過這個函數(shù)指針來實現(xiàn)的插入。
一個小小的技巧就實現(xiàn)了多態(tài)。而且它給出了 唯一值和時間類型的key 插入方法。能夠滿足一般需求,用戶也能夠?qū)崿F(xiàn)自己的插入方法。
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 唯一值類型插入
?
這個即為一般紅黑樹的插入方法,循環(huán),假設(shè)插入的值比當(dāng)前節(jié)點小,就進入左子樹,否則進入右子樹。直至遇到葉子結(jié)點。葉子節(jié)點就是要鏈入紅黑樹的位置。
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: }假設(shè)有相等的結(jié)點。會直接被覆蓋,如上圖插入key為2的結(jié)點,則當(dāng)tmp 為2的結(jié)點時。p為葉子遍歷結(jié)束。這樣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
也是依照《算法導(dǎo)論》上的步驟,先刪除后調(diào)整,在《手把手實現(xiàn)紅黑樹》已介紹,請參考
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.實戰(zhàn)
?
因為ngx_rbtree_t未牽涉到內(nèi)存池,所以很easy抽出來使用,例如以下為實現(xiàn)了插入、打印最小值、刪除的樣例
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: }?
執(zhí)行結(jié)果:
-
Echo Chen:Blog.csdn.net/chen19870707
-
版權(quán)聲明:本文博客原創(chuàng)文章,博客,未經(jīng)同意,不得轉(zhuǎn)載。
總結(jié)
以上是生活随笔為你收集整理的新秀nginx源代码分析数据结构篇(四)红黑树ngx_rbtree_t的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 在RHEL6.6环境下进行LVS-NAT
- 下一篇: 架构师速成8.3-架构师必须要了解的规则