前言
??剛開始接觸紅黑樹的時(shí)候,感覺很難。其實(shí)不然,紅黑樹只是情況分的多了一點(diǎn)而已,相較于線段樹,主席樹等等,簡(jiǎn)單多了。對(duì)于紅黑樹3種插入維護(hù)4種刪除維護(hù)沒必要去記憶,稍作了解,對(duì)于紅黑樹的性質(zhì)和特點(diǎn),需要特別記憶。
??本專欄知識(shí)點(diǎn)是通過零聲教育的線上課學(xué)習(xí),進(jìn)行梳理總結(jié)寫下文章,對(duì)c/c++linux課程感興趣的讀者,可以點(diǎn)擊鏈接:C/C++Linux服務(wù)器開發(fā)/后臺(tái)架構(gòu)師【零聲教育】-學(xué)習(xí)視頻教程-騰訊課堂課程介紹詳細(xì)查看課程的服務(wù)。
注意,本文圖中紅黑樹的葉子結(jié)點(diǎn)默認(rèn)不畫出來~
為什么要有紅黑樹
二叉搜索樹
??二叉搜索樹(又叫二叉排序樹,BST):對(duì)于任意一個(gè)結(jié)點(diǎn),其左子樹的值必定小于該結(jié)點(diǎn),其右子樹的值必定大于該結(jié)點(diǎn)。那么中序遍歷的時(shí)候,就是有序的了。理論上來說,增加,刪除,修改的時(shí)間復(fù)雜度都是O(log(N))。但是它存在一個(gè)致命的問題。
??退化:向樹中插入[1,2,3,4,5,6],此時(shí)樹退化成了一個(gè)鏈表,增加,刪除,修改的時(shí)間復(fù)雜度退化為O(N)
添加圖片注釋,不超過 140 字(可選)
平衡二叉搜索樹
??平衡二叉搜索樹(AVL Tree):它的左右兩個(gè)子樹的高度差的絕對(duì)值不超過1,并且左右兩個(gè)子樹都是一棵平衡二叉搜索樹。如果向樹中插入[1,2,3,4,5,6]
添加圖片注釋,不超過 140 字(可選)
可以看到AVLTree在最壞的情況下,依然保持了“絕對(duì)的平衡”:左右兩個(gè)子樹的高度差的絕對(duì)值不超過1。那么AVL Tree是如何保證平衡的呢,是通過旋轉(zhuǎn),可以看到,無論是插入還是刪除元素,都要去通過旋轉(zhuǎn)維護(hù)整個(gè)樹的平衡。
- AVL查詢?cè)兀篛(log(N))
- AVL插入元素:最多一次旋轉(zhuǎn)O(1),加上查詢的時(shí)間O(log(N)),插入的復(fù)雜度O(log(N))
- AVL刪除元素:必須檢查從刪除結(jié)點(diǎn)開始到根結(jié)點(diǎn)路徑上的所有結(jié)點(diǎn)的平衡因子。因此刪除的代價(jià)比較大,刪除最多需要log(N)次旋轉(zhuǎn),加上查詢的時(shí)間,刪除的復(fù)雜度O(2log(N))
紅黑樹
??我們發(fā)現(xiàn),AVL樹未免太嚴(yán)格了一些,有沒有一種數(shù)據(jù)結(jié)構(gòu),能讓AVL樹不那么嚴(yán)格平衡,降低維護(hù)平衡的開銷,同時(shí)又不能像BST一樣退化呢?
當(dāng)然有,就是本文所寫的紅黑樹(rbTree):
- rbTree查詢?cè)兀篛(log(N))
- rbTree插入元素:插入最多2次旋轉(zhuǎn),加上查詢的時(shí)間O(log(N)),插入的復(fù)雜度O(log(N))
- rbTree刪除元素:刪除最多需要3次旋轉(zhuǎn),加上查詢的時(shí)間,刪除的復(fù)雜度O(log(N))
??雖然插入和刪除元素后,需要旋轉(zhuǎn)和變色(本文中統(tǒng)一為維護(hù)),但是這一時(shí)間復(fù)雜度可以估算為O(1)不計(jì)
??因?yàn)閞bTree的第6條性質(zhì)(見下文)
- 所以紅黑樹的查詢效率略低與AVL的查詢
- 紅黑樹和AVL插入的速度差不多
- 紅黑樹刪除的速度比AVL快,因?yàn)锳VL刪除最多需要og(N)次旋轉(zhuǎn)
紅黑樹的應(yīng)用場(chǎng)景
紅黑樹的性質(zhì)(重點(diǎn))
紅黑樹的定義
#define RED 0#define BlACK 1typedef int KEY_TYPE;typedef struct _rbtree_node { unsigned char color;//顏色 struct _rbtree_node *left;//左子樹 struct _rbtree_node *right;//右子樹 struct _rbtree_node *parent;//父結(jié)點(diǎn) KEY_TYPE key; void *value;} rbtree_node;//紅黑樹結(jié)點(diǎn)typedef struct _rbtree { rbtree_node *root;//根結(jié)點(diǎn) rbtree_node *nil;//通用葉子結(jié)點(diǎn)} rbtree;//紅黑樹
紅黑樹的左旋與右旋
動(dòng)三個(gè)方向,改6個(gè)指針。 通過旋轉(zhuǎn),調(diào)整左右高度,使樹達(dá)到平衡。
添加圖片注釋,不超過 140 字(可選)
左旋leftRotate(T,x)—中右->左中
降低X結(jié)點(diǎn)的高度,提高X結(jié)點(diǎn)右結(jié)點(diǎn)(即Y)的高度。
添加圖片注釋,不超過 140 字(可選)
右旋rightRotate(T,y)—中左->中右
降低Y結(jié)點(diǎn)的高度,提高Y結(jié)點(diǎn)左結(jié)點(diǎn)(即X)的高度。
添加圖片注釋,不超過 140 字(可選)
//左旋leftRotate(T,x)—中右->左中//降低X結(jié)點(diǎn)的高度,提高X結(jié)點(diǎn)右結(jié)點(diǎn)(即Y)的高度。void _left_rotate(rbtree *T, rbtree_node *x) { rbtree_node *y = x->right; //1 x->right = y->left;//x的右子樹指向y的左子樹 if (y->left != T->nil) { y->left->parent = x;//y的左子樹的父節(jié)點(diǎn)指向x } //2 y->parent = x->parent;//y的父結(jié)點(diǎn)指向x的父結(jié)點(diǎn) if (x->parent == T->nil) {//如果x是根結(jié)點(diǎn) T->root = y; } else if (x == x->parent->left) { x->parent->left = y;//本來指向x結(jié)點(diǎn)的父指針,改成指向y } else { x->parent->right = y; } //3 y->left = x;//y的左子樹指向x結(jié)點(diǎn) x->parent = y;//x的父節(jié)點(diǎn)指向y}//右旋//copy左旋的代碼//left改成right,right改成left//x改成y,y改成xvoid _right_rotate(rbtree *T, rbtree_node *y) { rbtree_node *x = y->left; //1 y->left = x->right; if (x->right != T->nil) { x->right->parent = y; } //2 x->parent = y->parent; if (y->parent == T->nil) { T->root = x; } else if (y == y->parent->right) { y->parent->right = x; } else { y->parent->left = x; } //3 x->right = y; y->parent = x;}
紅黑樹插入結(jié)點(diǎn)與插入維護(hù)紅黑樹的三種情況
插入結(jié)點(diǎn)
??在插入結(jié)點(diǎn)時(shí),我們始終認(rèn)為“插入這個(gè)結(jié)點(diǎn)之前,原來的紅黑樹是滿足紅黑樹性質(zhì)的==”,那么插入的位置容易找,就是不斷的對(duì)比key,最終找到位置,那么新增的結(jié)點(diǎn)是什么顏色呢?我們通過性質(zhì)發(fā)現(xiàn):
- 如果新結(jié)點(diǎn)是黑色,違背了第5條性質(zhì)
- 如果新結(jié)點(diǎn)是紅色,可能違背第4條性質(zhì)
而第四條性質(zhì),我們可以通過旋轉(zhuǎn)與上色的方式修復(fù),所以在我們插入結(jié)點(diǎn)的時(shí)候,我們始終認(rèn)為新結(jié)點(diǎn)是紅色
//因?yàn)閭魅氲膋ey可能是字符,可能是整形,所以要提取出來//這里可以看出,其實(shí)可以封裝成一個(gè)模板int key_compare(KEY_TYPE a, KEY_TYPE b) { //這里假設(shè)是int if (a > b) { return 1; } else if (a root; rbtree_node *y = T->nil;//y是x的父節(jié)點(diǎn) while (x != T->nil) {//二分找位置 y = x; if (key_compare(z->key, x->key) left; } else if (key_compare(z->key, x->key) > 0) { x = x->right; } else { //如果key相等,看自己的業(yè)務(wù)情景 //重復(fù)插入可以不修改直接退出,可以修改val return; } } //插入 z->parent = y; if (y == T->nil) { T->root = z; } else if (key_compare(z->key, y->key) left = z; } else { y->right = z; } z->left = T->nil; z->right = T->nil; z->color = RED; //維護(hù)紅黑樹 rbtree_insert_fixup(T, z);}
插入結(jié)點(diǎn)后維護(hù)紅黑樹
??我們知道新增結(jié)點(diǎn)是紅色,如果新結(jié)點(diǎn)是父節(jié)點(diǎn)也是紅色,那么就需要維護(hù)紅黑樹了。
??如果父結(jié)點(diǎn)是爺結(jié)點(diǎn)是左子樹,可以歸納出來三種情況。同理如果父結(jié)點(diǎn)是爺結(jié)點(diǎn)是右子樹,我們也可以歸納出來三種情況。但是這三種情況的差異就是旋轉(zhuǎn)方向的區(qū)別而已。一共是6種情況,但是歸納出來其實(shí)是3種,讀者不要搞錯(cuò)了。
在下面的圖中:z表示新增結(jié)點(diǎn),y表示叔結(jié)點(diǎn)
父結(jié)點(diǎn)是爺結(jié)點(diǎn)是左子樹
1. 叔結(jié)點(diǎn)是紅色的
- 將叔結(jié)點(diǎn)和父結(jié)點(diǎn)變黑,爺結(jié)點(diǎn)變紅
- 將當(dāng)前結(jié)點(diǎn)變成爺結(jié)點(diǎn)(因?yàn)闋斀Y(jié)點(diǎn)是紅,爺結(jié)點(diǎn)的父節(jié)點(diǎn)也可能是紅,所以要遞歸維護(hù))
添加圖片注釋,不超過 140 字(可選)
2. 叔結(jié)點(diǎn)是黑色的且新結(jié)點(diǎn)是左子樹
- 將父結(jié)點(diǎn)變成黑色,爺結(jié)點(diǎn)變成紅色
- 以爺結(jié)點(diǎn)為中心右旋
添加圖片注釋,不超過 140 字(可選)
3. 叔結(jié)點(diǎn)是黑色的且新結(jié)點(diǎn)是右子樹
- 以父結(jié)點(diǎn)為中心左旋
- 將父結(jié)點(diǎn)變黑色,爺結(jié)點(diǎn)變紅色
- 以爺結(jié)點(diǎn)為中心右旋
添加圖片注釋,不超過 140 字(可選)
父結(jié)點(diǎn)是爺結(jié)點(diǎn)是右子樹
1. 叔結(jié)點(diǎn)是紅色的
- 將叔結(jié)點(diǎn)和父結(jié)點(diǎn)變黑,爺結(jié)點(diǎn)變紅
- 將當(dāng)前結(jié)點(diǎn)變成爺結(jié)點(diǎn)(因?yàn)闋斀Y(jié)點(diǎn)是紅,爺結(jié)點(diǎn)的父節(jié)點(diǎn)也可能是紅,所以要遞歸維護(hù))
添加圖片注釋,不超過 140 字(可選)
2. 叔結(jié)點(diǎn)是黑色的且新結(jié)點(diǎn)是左子樹
- 以父結(jié)點(diǎn)為中心右旋
- 將父結(jié)點(diǎn)變黑色,爺結(jié)點(diǎn)變紅色
- 以爺結(jié)點(diǎn)為中心左旋
添加圖片注釋,不超過 140 字(可選)
3. 叔結(jié)點(diǎn)是黑色的且新結(jié)點(diǎn)是右子樹
- 將父結(jié)點(diǎn)變成黑色,爺結(jié)點(diǎn)變成紅色
- 以爺結(jié)點(diǎn)為中心左旋
添加圖片注釋,不超過 140 字(可選)
維護(hù)代碼
//修復(fù)第4條性質(zhì)void rbtree_insert_fixup(rbtree *T, rbtree_node *z) { while (z->parent->color == RED) {//父結(jié)點(diǎn)是紅色的,需要調(diào)整 if (z->parent == z->parent->parent->left) {//如果父結(jié)點(diǎn)是爺結(jié)點(diǎn)是左子樹 rbtree_node *y = z->parent->parent->right;//叔結(jié)點(diǎn) if (y->color == RED) {//叔結(jié)點(diǎn)是紅色的 //先變色,叔,父變黑 z->parent->color = BLACK; y->color = BLACK; //爺結(jié)點(diǎn)變紅 z->parent->parent->color = RED; //下面的調(diào)整完了,調(diào)整上面 z = z->parent->parent; } else {//叔父結(jié)點(diǎn)是黑色 if (z == z->parent->right) {//新節(jié)點(diǎn)是在右邊 z = z->parent; rbtree_left_rotate(T, z); } z->parent->color = BLACK; z->parent->parent->color = RED; rbtree_right_rotate(T, z->parent->parent); } } else { // 如果父結(jié)點(diǎn)是爺結(jié)點(diǎn)是右子樹 rbtree_node *y = z->parent->parent->left;//叔父結(jié)點(diǎn) if (y->color == RED) {//叔父結(jié)點(diǎn)是紅色的 //先變色,叔,父變黑 z->parent->color = BLACK; y->color = BLACK; //爺結(jié)點(diǎn)變紅 z->parent->parent->color = RED; //下面的調(diào)整完了,調(diào)整上面 z = z->parent->parent; } else {//叔父結(jié)點(diǎn)是黑色 if (z == z->parent->left) {//新節(jié)點(diǎn)是在左邊 z = z->parent; rbtree_right_rotate(T, z); } z->parent->color = BLACK; z->parent->parent->color = RED; rbtree_left_rotate(T, z->parent->parent); } } } //最后別忘了根節(jié)點(diǎn)始終是黑色 T->root->color = BLACK;}
紅黑樹刪除結(jié)點(diǎn)與刪除維護(hù)紅黑樹的四種情況
刪除結(jié)點(diǎn)
我們定義:
- 覆蓋結(jié)點(diǎn):z(被指定刪除的結(jié)點(diǎn),實(shí)際上被覆蓋)
- 刪除結(jié)點(diǎn):y(實(shí)際上被刪除的結(jié)點(diǎn),一般是后繼結(jié)點(diǎn))
- 軸心結(jié)點(diǎn):x(維護(hù)紅黑樹的結(jié)點(diǎn))
紅黑樹刪除結(jié)點(diǎn)根據(jù)改結(jié)點(diǎn)的左右子樹分為三種情況:
對(duì)不同情況的處理:
- 情況1:直接刪除該結(jié)點(diǎn)
- 情況2:將該結(jié)點(diǎn)的唯一子樹掛到父結(jié)點(diǎn)上,然后刪除該結(jié)點(diǎn)
- 情況3:找一個(gè)刪除結(jié)點(diǎn)y(后繼結(jié)點(diǎn))覆蓋 指定結(jié)點(diǎn)z,然后刪除 刪除結(jié)點(diǎn)y,對(duì)于這個(gè)刪除結(jié)點(diǎn)y來說,它的情況一定是情況1或情況2
刪除代碼
rbtree_node *rbtree_mini(rbtree *T, rbtree_node *x) { while (x->left != T->nil) { x = x->left; } return x;}//找后繼結(jié)點(diǎn)rbtree_node *rbtree_successor(rbtree *T, rbtree_node *x) { rbtree_node *y = x->parent; //右子樹不為空,則找右子樹中最左的元素 if (x->right != T->nil) { return rbtree_mini(T, x->right); } //找到結(jié)點(diǎn)第一個(gè)父結(jié)點(diǎn) while ((y != T->nil) && (x == y->right)) { x = y; y = y->parent; } return y;}//覆蓋結(jié)點(diǎn)z//刪除結(jié)點(diǎn)y//軸心結(jié)點(diǎn)xrbtree_node *rbtree_delete(rbtree *T, rbtree_node *z) { rbtree_node *y = T->nil; rbtree_node *x = T->nil; if ((z->left == T->nil) || (z->right == T->nil)) { y = z;//如果沒有孩子或只有一個(gè) } else {//如果有兩個(gè)子樹則找后繼 y = rbtree_successor(T, z); } //一般x是y的右子樹,找到軸心結(jié)點(diǎn) if (y->left != T->nil) { x = y->left; } else if (y->right != T->nil) { x = y->right; } //將該結(jié)點(diǎn)的唯一子樹掛到父結(jié)點(diǎn)上,然后刪除該結(jié)點(diǎn) x->parent = y->parent; if (y->parent == T->nil) {//根結(jié)點(diǎn) T->root = x; } else if (y == y->parent->left) { y->parent->left = x; } else { y->parent->right = x; } //進(jìn)行覆蓋操作 if (y != z) { z->key = y->key; z->value = y->value; } //黑色才需要調(diào)整 if (y->color == BLACK) { rbtree_delete_fixup(T, x); } return y;}
刪除結(jié)點(diǎn)后維護(hù)紅黑樹
??想一想,刪除一個(gè)結(jié)點(diǎn),該結(jié)點(diǎn)是什么顏色的時(shí)候才需要維護(hù)紅黑樹呢?
- 如果是紅色,沒有違反任何性質(zhì)。所以如果是紅色直接刪除即可,無需維護(hù)
- 如果是黑色,黑色被刪除,那么必定違反第5條性質(zhì),破壞了黑高,所以我們需要針對(duì)這一情況進(jìn)行維護(hù)
??如果當(dāng)前結(jié)點(diǎn)是父結(jié)點(diǎn)的左子樹的情況,可以歸納出來四種情況。同理如果當(dāng)前結(jié)點(diǎn)是父結(jié)點(diǎn)的右子樹,我們也可以歸納出來四種情況。但是這四種情況的差異就是旋轉(zhuǎn)方向的區(qū)別而已(鏡像的)。一共是8種情況,但是歸納出來其實(shí)是4種,讀者不要搞錯(cuò)了。
當(dāng)前結(jié)點(diǎn)是父結(jié)點(diǎn)的左子樹的情況
1.當(dāng)前結(jié)點(diǎn)的兄弟結(jié)點(diǎn)是紅色的
- 兄弟節(jié)點(diǎn)變成黑色
- 父節(jié)點(diǎn)變成紅色
- 父節(jié)點(diǎn)做左旋
- 將兄弟結(jié)點(diǎn)調(diào)整為父節(jié)點(diǎn)的右子樹
添加圖片注釋,不超過 140 字(可選)
2. 當(dāng)前結(jié)點(diǎn)的兄弟結(jié)點(diǎn)是黑色的,而且兄弟結(jié)點(diǎn)的兩個(gè)孩子結(jié)點(diǎn)都是黑色的
- 兄弟節(jié)點(diǎn)變成紅色
- 軸心結(jié)點(diǎn)變?yōu)楦腹?jié)點(diǎn)
添加圖片注釋,不超過 140 字(可選)
3. 當(dāng)前結(jié)點(diǎn)的兄弟結(jié)點(diǎn)是黑色的,而且兄弟結(jié)點(diǎn)的左孩子是紅色的,右孩子是黑色的
- 將左孩子涂黑
- 將兄弟節(jié)點(diǎn)變紅
- 對(duì)兄弟節(jié)點(diǎn)右旋
- 將兄弟結(jié)點(diǎn)調(diào)整為父節(jié)點(diǎn)的右子樹
- 現(xiàn)在情況3就會(huì)變成情況4,接著做情況4的步驟
添加圖片注釋,不超過 140 字(可選)
4. 當(dāng)前結(jié)點(diǎn)的兄弟結(jié)點(diǎn)是黑色的,而且兄弟結(jié)點(diǎn)的左孩子是黑色的,右孩子是紅色的
- 將兄弟節(jié)點(diǎn)換成父節(jié)點(diǎn)的顏色
- 把父節(jié)點(diǎn)和兄弟節(jié)點(diǎn)的右孩子涂黑
- 對(duì)父節(jié)點(diǎn)做左旋
- 設(shè)置x指針,指向根節(jié)點(diǎn)
添加圖片注釋,不超過 140 字(可選)
當(dāng)前結(jié)點(diǎn)是父結(jié)點(diǎn)的右子樹的情況
1. 當(dāng)前結(jié)點(diǎn)的兄弟結(jié)點(diǎn)是紅色的
- 兄弟節(jié)點(diǎn)變成黑色
- 父節(jié)點(diǎn)變成紅色
- 父節(jié)點(diǎn)做右旋
- 將兄弟結(jié)點(diǎn)調(diào)整為父節(jié)點(diǎn)的左子樹
2. 當(dāng)前結(jié)點(diǎn)的兄弟結(jié)點(diǎn)是黑色的,而且兄弟結(jié)點(diǎn)的兩個(gè)孩子結(jié)點(diǎn)都是黑色的
- 兄弟節(jié)點(diǎn)變成紅色
- 軸心結(jié)點(diǎn)變?yōu)楦腹?jié)點(diǎn)
3. 當(dāng)前結(jié)點(diǎn)的兄弟結(jié)點(diǎn)是黑色的,而且兄弟結(jié)點(diǎn)的左孩子是黑色的,右孩子是紅色的
- 將右孩子變黑
- 將兄弟節(jié)點(diǎn)變紅
- 對(duì)兄弟結(jié)點(diǎn)左旋
- 將兄弟結(jié)點(diǎn)調(diào)整為父節(jié)點(diǎn)的左子樹
- 現(xiàn)在情況3就會(huì)變成情況4,接著做情況4的步驟
4. 當(dāng)前結(jié)點(diǎn)的兄弟結(jié)點(diǎn)是黑色的,而且兄弟結(jié)點(diǎn)的左孩子是紅色的,右孩子是黑色的
- 將兄弟節(jié)點(diǎn)換成父節(jié)點(diǎn)的顏色
- 把父節(jié)點(diǎn)和兄弟節(jié)點(diǎn)的左孩子變黑
- 對(duì)父節(jié)點(diǎn)做右旋
- 將軸心結(jié)點(diǎn)調(diào)整為根結(jié)點(diǎn)
維護(hù)代碼
void rbtree_delete_fixup(rbtree *T, rbtree_node *x) { //如果x是紅色,變成黑色,如果x是黑色,需要調(diào)整 while ((x != T->root) && (x->color == BLACK)) { //當(dāng)前結(jié)點(diǎn)是父結(jié)點(diǎn)的左子樹 if (x == x->parent->left) { //兄弟節(jié)點(diǎn) rbtree_node *w = x->parent->right; // 情況1:兄弟節(jié)點(diǎn)為紅色 if (w->color == RED) { // 兄弟節(jié)點(diǎn)變成黑色 w->color = BLACK; // 父節(jié)點(diǎn)變成紅色 x->parent->color = RED; // 父節(jié)點(diǎn)做左旋 rbtree_left_rotate(T, x->parent); //將兄弟結(jié)點(diǎn)調(diào)整為父節(jié)點(diǎn)的右子樹 w = x->parent->right; } // 情況2:兄弟節(jié)點(diǎn)是黑色的,且兄弟的左孩子和右孩子都是黑色 if ((w->left->color == BLACK) && (w->right->color == BLACK)) { // 兄弟節(jié)點(diǎn)變成紅色 w->color = RED; // 軸心結(jié)點(diǎn)變?yōu)楦腹?jié)點(diǎn) x = x->parent; } else { // 情況3:x的兄弟節(jié)點(diǎn)是黑色的,兄弟的左孩子是紅色,右孩子是黑色 if (w->right->color == BLACK) { // 將左孩子涂黑 w->left->color = BLACK; // 將兄弟節(jié)點(diǎn)變紅 w->color = RED; // 對(duì)兄弟節(jié)點(diǎn)右旋 rbtree_right_rotate(T, w); // 重新設(shè)置x的兄弟節(jié)點(diǎn) w = x->parent->right; } // 情況4:x的兄弟節(jié)點(diǎn)是黑色;x的兄弟節(jié)點(diǎn)的右孩子是紅色的 // 將兄弟節(jié)點(diǎn)換成父節(jié)點(diǎn)的顏色 w->color = x->parent->color; // 把父節(jié)點(diǎn)和兄弟節(jié)點(diǎn)的右孩子涂黑 x->parent->color = BLACK; w->right->color = BLACK; // 對(duì)父節(jié)點(diǎn)做左旋 rbtree_left_rotate(T, x->parent); // 設(shè)置x指針,指向根節(jié)點(diǎn) x = T->root; } } else {//當(dāng)前結(jié)點(diǎn)是父結(jié)點(diǎn)的右子樹 //兄弟節(jié)點(diǎn) rbtree_node *w = x->parent->left; //情況1:兄弟結(jié)點(diǎn)為紅色 if (w->color == RED) { // 兄弟節(jié)點(diǎn)變成黑色 w->color = BLACK; // 父節(jié)點(diǎn)變成紅色 x->parent->color = RED; // 父節(jié)點(diǎn)做右旋 rbtree_right_rotate(T, x->parent); //將兄弟結(jié)點(diǎn)調(diào)整為父節(jié)點(diǎn)的左子樹 w = x->parent->left; } // 情況2:兄弟節(jié)點(diǎn)是黑色的,且兄弟的左孩子和右孩子都是黑色 if ((w->left->color == BLACK) && (w->right->color == BLACK)) { // 兄弟節(jié)點(diǎn)變成紅色 w->color = RED; // 軸心結(jié)點(diǎn)變?yōu)楦腹?jié)點(diǎn) x = x->parent; } else { // 情況3:x的兄弟結(jié)點(diǎn)是黑色的,兄弟的左孩子是黑色,右孩子是紅色 if (w->left->color == BLACK) { //將右孩子變黑 w->right->color = BLACK; //將兄弟節(jié)點(diǎn)變紅 w->color = RED; //對(duì)兄弟結(jié)點(diǎn)左旋 rbtree_left_rotate(T, w); //將兄弟結(jié)點(diǎn)調(diào)整為父節(jié)點(diǎn)的左子樹 w = x->parent->left; } // 情況4:x的兄弟結(jié)點(diǎn)是黑色的,兄弟的左孩子是紅色,右孩子是黑色 // 將兄弟節(jié)點(diǎn)換成父節(jié)點(diǎn)的顏色 w->color = x->parent->color; // 把父節(jié)點(diǎn)和兄弟節(jié)點(diǎn)的左孩子變黑 x->parent->color = BLACK; w->left->color = BLACK; // 對(duì)父節(jié)點(diǎn)做右旋 rbtree_right_rotate(T, x->parent); //將軸心結(jié)點(diǎn)調(diào)整為根結(jié)點(diǎn) x = T->root; } } } // 繼承節(jié)點(diǎn)變?yōu)楹谏?,為了彌補(bǔ)失去的黑高 x->color = BLACK;}
紅黑樹的遍歷、查詢、測(cè)試
rbtree_node *rbtree_search(rbtree *T, KEY_TYPE key) { rbtree_node *node = T->root; while (node != T->nil) { if (key key) { node = node->left; } else if (key > node->key) { node = node->right; } else { return node; } } return T->nil;}void rbtree_traversal(rbtree *T, rbtree_node *node) { if (node != T->nil) { rbtree_traversal(T, node->left); printf(“key:%d, color:%d”, node->key, node->color); rbtree_traversal(T, node->right); }}int main() { int keyArray[20] = {24, 25, 13, 35, 23, 26, 67, 47, 38, 98, 20, 19, 17, 49, 12, 21, 9, 18, 14, 15}; rbtree *T = (rbtree *) malloc(sizeof(rbtree)); if (T == NULL) { printf(“malloc failed”); return -1; } T->nil = (rbtree_node *) malloc(sizeof(rbtree_node)); T->nil->color = BLACK; T->root = T->nil; rbtree_node *node = T->nil; int i = 0; for (i = 0; i key = keyArray[i]; node->value = NULL; rbtree_insert(T, node); } rbtree_traversal(T, T->root); printf(“—————————————-“); for (i = 0; i root); printf(“—————————————-“); }}
原文鏈接:隨處可見的紅黑樹詳解_cheems~的博客-CSDN博客