紅黑樹是一種二叉平衡樹,在每一個結點增加了一個存儲位表示結點的顏色,以維持它的平衡。
紅黑樹性質
(1)紅黑樹結點有如下域:color,key,left,right,p;我們把這些NIL結點是爲指向外結點的指針,可以自己定義;
(2)每一個結點不是紅的就是黑的,根節點和NIL結點都是黑的;
(3)如果一個節點是紅的,那麼它的父親和兩個孩子都是黑的;
(4)對於每一個結點,它到其子孫結點的所有路徑上的包含相同數目的black結點,並不要求父節點到每一層的黑高度相同;
注1:可以使用同一的一個NIL來表示所有的葉子結點和根部的父節點,這樣可以減少浪費內存,該NIL的父親就是上述的普通結點,這樣查找不成功時,就可以重新回到根節點root;
注2:設某個結點x出發到達任意一個節點的任意路徑上,黑結點的個數稱爲該節點的黑高度,bh(x)表示;可歸納證明出根節點至少有2^h-1個結點,故n>=2^h-1,求得h<=2lg(n+1);
紅黑樹插入
(1)爲了保持上述紅黑樹的性質(4),插入的節點z顏色一定是紅色,其父節點爲p;
(2)若剛開始樹爲空,則z是根節點,p爲NIL節點,則將z的結點改爲黑色;
(3)若p的顏色是黑色,則直接插入,因爲紅黑樹的性質完全保持;
(4)若p的顏色是紅色,違反紅黑樹的性質3,此時要討論情況;
(4.1)若p是p父親的左孩子;
(4.1.1)若p的兄弟節點ps是紅色,則改變p和ps爲黑色,p父節點改爲紅色,此時p父節點爲新的z,繼續判別;
(4.1.2)若p的兄弟節點ps是黑色,且z是父親的右孩子,做一次左旋,到情況(4.1.3);
(4.1.3)若p的兄弟節點ps是黑色,且z是父親的左孩子,做一次右旋,改變z父親顏色爲黑色,z祖父顏色爲紅色,完畢;
(4.2)若p是p父親的右孩子;情況與(4.1)類似,執行相反的動作;
(5)始終將根節點的顏色置爲黑,因爲處理時根節點有可能變紅;
(6)時間複雜度爲O(lgn),空間複雜度爲O(1);
紅黑樹刪除
(1)首先根據二叉平衡樹的刪除原理,找到被刪除的節點y,x是y的子女(肯定只有一個),x會連接到y的父親上;
(2)若y爲紅色,直接刪掉,因爲紅黑樹的性質保持不變;
(3)若y爲黑色節點,x爲紅色,則直接刪掉y,將x變爲黑色;
(4)若y爲黑色節點,x(可以爲NIl節點)爲黑色,則刪掉y,將x變爲雙黑色;
(4.1)若x是x父親的左孩子,分別有四種情況來討論;
(4.1.1)若x的兄弟是紅色,則左旋一次,改變兄弟顏色爲黑,父親顏色爲紅,則它仍爲新的x,繼續;
(4.1.2)若x的兄弟是黑色,且x兄弟的兩個孩子都是黑色,則去掉x的一重黑色,將兄弟顏色變爲紅,將去掉的一重黑色,給x的父親,若x父親是紅色,則直接改成黑色,否則將變成雙黑,成爲新的x,繼續;
(4.1.3)若x的兄弟是黑色,且x兄弟的右孩子黑色,左孩子是紅色,則先將x兄弟那一塊右旋,改色,使得x兄弟的右孩子變爲紅色;進入(4.1.4);
(4.1.4)若x的兄弟是黑色,且x兄弟的右孩子紅色,左孩子可任意色,則左旋,改變x兄弟右孩子顏色爲黑色,兄弟變爲父親的顏色,父親變爲黑色,自己去掉一重顏色,完畢;
(4.2)若x是x父親的右孩子,分別也有四種情況來討論,但與(4.1)對稱,相反來處理;
(5)若根爲雙黑,直接變爲單黑,返回;
(6)時間複雜度爲O(lgn),空間複雜度爲O(1);
紅黑樹定義:
typedef struct ngx_rbtree_node_s ngx_rbtree_node_t;
//紅黑樹節點的定義
struct ngx_rbtree_node_s {
ngx_rbtree_key_t key; //關鍵字
ngx_rbtree_node_t *left; //左子節點
ngx_rbtree_node_t *right; //右子節點
ngx_rbtree_node_t *parent;//父節點
u_char color; //顏色,非紅即黑
u_char data; //1個字節的節點數據,空間較小很少使用;
};
typedef struct ngx_rbtree_s ngx_rbtree_t;
//爲解決不同節點含有相同關鍵字元素衝突問題,設置該指針,靈活的添加衝突元素
typedef void (*ngx_rbtree_insert_pt) (ngx_rbtree_node_t *root,
ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);
//紅黑樹的定義
struct ngx_rbtree_s {
ngx_rbtree_node_t *root; //指向樹的根節點,根結點也是數據元素
ngx_rbtree_node_t *sentinel; //指向哨兵NIL節點
ngx_rbtree_insert_pt insert; //添加元素的函數指針
};
左旋轉和右旋轉
//左旋轉
static ngx_inline void
ngx_rbtree_left_rotate(ngx_rbtree_node_t **root, ngx_rbtree_node_t *sentinel,
ngx_rbtree_node_t *node)
{
ngx_rbtree_node_t *temp;
temp = node->right; //要旋轉節點的右孩子p
node->right = temp->left; //當前節點的右孩子指向改變p的左孩子
if (temp->left != sentinel) { //不是NIL節點時
temp->left->parent = node; //修改父親的指向
}
temp->parent = node->parent; //p指向要旋轉節點的父親
if (node == *root) { //如果當前旋轉節點的是根結點,那麼根指向要改變
*root = temp;
} else if (node == node->parent->left) { //左孩子時
node->parent->left = temp; //新的左孩子
} else {
node->parent->right = temp; //新的右孩子
}
temp->left = node; //p的左孩子指向
node->parent = temp; //父親改變
}
//右旋轉,與上述左邊旋轉對稱
static ngx_inline void
ngx_rbtree_right_rotate(ngx_rbtree_node_t **root, ngx_rbtree_node_t *sentinel,
ngx_rbtree_node_t *node)
{
ngx_rbtree_node_t *temp;
temp = node->left;
node->left = temp->right;
if (temp->right != sentinel) {
temp->right->parent = node;
}
temp->parent = node->parent;
if (node == *root) {
*root = temp;
} else if (node == node->parent->right) {
node->parent->right = temp;
} else {
node->parent->left = temp;
}
temp->right = node;
node->parent = temp;
}
插入節點:
void
ngx_rbtree_insert_value(ngx_rbtree_node_t *temp, ngx_rbtree_node_t *node,
ngx_rbtree_node_t *sentinel)
{
//temp爲初始時爲指向根節點
ngx_rbtree_node_t **p;
for ( ;; ) {
p = (node->key < temp->key) ? &temp->left : &temp->right; //目標節點的值小於當前節點的值時,向左走即走向左節點,否則向右
if (*p == sentinel) { //一直找到該節點的孩子指向的是NIL節點,直接break
break;
}
temp = *p; //改變方向
}
//注意使用地址的地址,才能改變某節點的孩子的指向,否則改變不了
*p = node; //該處指向新的節點,*p(某節點的孩子)本來指向sentinel,現在指向node了
node->parent = temp;
node->left = sentinel; //指向NIL
node->right = sentinel;
ngx_rbt_red(node); //插入的節點均爲紅色
}
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; //取出NIL節點
if (*root == sentinel) { //說明此時紅黑樹爲空
node->parent = NULL;
node->left = sentinel; //指向NIL
node->right = sentinel;
ngx_rbt_black(node); //node節點直接置爲黑
*root = node; //根節點直接指向該節點
return;
}
tree->insert(*root, node, sentinel);
//插入節點,如ngx_rbtree_insert_value(ngx_rbtree_node_t *temp, ngx_rbtree_node_t *node, ngx_rbtree_node_t *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)) {
//對應算法導論第三版P179的情況1
ngx_rbt_black(node->parent); //當前節點的父親變黑
ngx_rbt_black(temp); //當前節點的父親的右兄弟變黑
ngx_rbt_red(node->parent->parent);//當前節點的父親的父親變紅
node = node->parent->parent; //當前節點變爲該當前節點的父親的父親
} else { //當前節點的父親的右兄弟爲黑色時
//對應算法導論第三版P179的情況2
//當前節點是右節點時,先左旋轉
if (node == node->parent->right) {
node = node->parent; //當節點變爲父節點
ngx_rbtree_left_rotate(root, sentinel, node); //當前節點左旋轉
}
//對應算法導論第三版P179的情況3
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); //最後始終着色根結點爲黑色
}
刪除節點:
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;
//subst爲要替代的節點
//temp爲將要替代節點的孩子
if (node->left == sentinel) { //刪除節點爲左孩子爲NIL節點
temp = node->right;
subst = node;
} else if (node->right == sentinel) {
temp = node->left;
subst = node;
} else {
subst = ngx_rbtree_min(node->right, sentinel); //找到以刪除節點右孩子爲根的最小值節點
if (subst->left != sentinel) {
temp = subst->left;
} else { //通過ngx_rbtree_min找到的,該最小值節點的左孩子一定是NIL
temp = subst->right;
}
}
if (subst == *root) { //subst爲要刪除的節點爲根節點時
*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); //直接取顏色
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) { //通過ngx_rbtree_min一開始就不成立,即node->right的left爲NIL節點
temp->parent = subst;
} else {
temp->parent = subst->parent; //通過ngx_rbtree_min找到的
}
subst->left = node->left; //指向node的左孩子
subst->right = node->right; //指向node的右孩子
subst->parent = node->parent;
ngx_rbt_copy_color(subst, node); //將node節點的顏色複製給subst節點
if (node == *root) {
*root = subst; //指向新的根節點
} else {
if (node == node->parent->left) {
node->parent->left = subst; //改變父親孩子指向到subst
} else {
node->parent->right = subst;
}
}
if (subst->left != sentinel) { //改變subst孩子的指向
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) { //如果刪除的節點的顏色爲紅色,直接返回
return;
}
//平衡
/* a delete fixup */
//孩子節點,如果孩子節點爲黑色時
while (temp != *root && ngx_rbt_is_black(temp)) {
if (temp == temp->parent->left) { //temp爲左孩子時
w = temp->parent->right; //temp的右兄弟
//算法導論第三版P186中的情況a
if (ngx_rbt_is_red(w)) { //temp的右兄弟爲紅色時
ngx_rbt_black(w); //temp的右兄弟變爲黑色
ngx_rbt_red(temp->parent); //temp的父親變爲紅色色
ngx_rbtree_left_rotate(root, sentinel, temp->parent); //以temp->parent左旋轉
w = temp->parent->right; //新的右兄弟,將會到以下幾種情況
}
//算法導論第三版P186中的情況b
//當temp的右兄弟的兩個孩子均爲黑色時
if (ngx_rbt_is_black(w->left) && ngx_rbt_is_black(w->right)) {
ngx_rbt_red(w); //將右兄弟變爲紅色
temp = temp->parent; //將以新的temp進行新的一次循環
} else {
//算法導論第三版P186中的情況c
if (ngx_rbt_is_black(w->right)) { //如果右兄弟的右孩子是黑色的
ngx_rbt_black(w->left); //w的左孩子變黑
ngx_rbt_red(w); //w變紅
ngx_rbtree_right_rotate(root, sentinel, w); //以w右旋
w = temp->parent->right; //新的右兄弟,直接執行情況c
}
//算法導論第三版P186中的情況d
ngx_rbt_copy_color(w, temp->parent); //右兄弟變成父親的顏色
ngx_rbt_black(temp->parent); //temp的父親變黑
ngx_rbt_black(w->right); //右兄弟的右孩子變黑
ngx_rbtree_left_rotate(root, sentinel, temp->parent); //以temp的父親右旋
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;
}
}
}
//temp爲紅色時,直接將temp置爲黑色
ngx_rbt_black(temp);
}