Rbtree.h文件中數據結構及函數
rb_node
紅黑樹節點數據結構中使用成員rb_parent_color同時存儲兩種數據,一是其雙親結點的地址,另一是此結點的着色。__attribute__((aligned(sizeof(long))))屬性保證了紅黑樹中的每個結點的首地址都是32位對齊的(在32位機上),也就是說每個結點首地址的bit[1]和bit[0]都是0,因此就可以使用bit[0]來存儲結點的顏色屬性而不干擾到其雙親結點首地址的存儲。
struct rb_node { /*高30位存儲雙親節點地址,最後一位爲此節點顏色,低兩位默認爲0,表示爲紅色,若最後一位爲1則表示爲黑色*/ unsigned long rb_parent_color; #define RB_RED 0 #define RB_BLACK 1 struct rb_node *rb_right; /*右子節點*/ struct rb_node *rb_left; /*左子結點*/ } __attribute__((aligned(sizeof(long)))); |
rb_root
/*紅黑樹的根節點*/ struct rb_root { struct rb_node *rb_node; }; |
rb_parent_color操作函數
/*獲取雙親節點的地址和此節點顏色,& ~3表示將倒數兩位位清0,其餘位保持不變*/ #define rb_parent(r) ((struct rb_node *)((r)->rb_parent_color & ~3)) /*獲取此節點的顏色,最低位表示顏色,1表示黑色,0表示紅色*/ #define rb_color(r) ((r)->rb_parent_color & 1) /*判斷此節點是否爲紅色*/ #define rb_is_red(r) (!rb_color(r)) /*判斷此節點是否爲黑色*/ #define rb_is_black(r) rb_color(r) /*將此節點設置爲紅色,&~1表示將最後一位清0,其餘位不變*/ #define rb_set_red(r) do { (r)->rb_parent_color &= ~1; } while (0) /*將此節點設置爲黑色*/ #define rb_set_black(r) do { (r)->rb_parent_color |= 1; } while (0) |
rb_set_parent()
/*設置雙親節點地址的函數,首先&3保持低兩位不變,將高30位清0,然後將P設置爲雙親地址*/ static inline void rb_set_parent(struct rb_node *rb, struct rb_node *p) { rb->rb_parent_color = (rb->rb_parent_color & 3) | (unsigned long)p; } |
rb_set_color()
/*設置結點顏色屬性的函數,先&~1將最低位清0,再獲取color的顏色屬性*/ static inline void rb_set_color(struct rb_node *rb, int color) { rb->rb_parent_color = (rb->rb_parent_color & ~1) | color; } |
/*初始化指向紅黑樹根結點的指針*/ #define RB_ROOT (struct rb_root) { NULL, } /*用來獲得包含struct rb_node的結構體的首地址 */ #define rb_entry(ptr, type, member) container_of(ptr, type, member) /*判斷樹是否爲空*/ #define RB_EMPTY_ROOT(root) ((root)->rb_node == NULL) /*判斷節點的雙親節點是否爲自身*/ #define RB_EMPTY_NODE(node) (rb_parent(node) == node) /*設置節點的雙親節點爲自身*/ #define RB_CLEAR_NODE(node) (rb_set_parent(node, node)) |
rb_init_node()
/*初始化新節點*/ static inline void rb_init_node(struct rb_node *rb) { rb->rb_parent_color = 0; rb->rb_right = NULL; rb->rb_left = NULL; RB_CLEAR_NODE(rb); } |
rb_link_node()
/*將節點作爲子節點連入紅黑樹*/ static inline void rb_link_node(struct rb_node * node, struct rb_node * parent, struct rb_node ** rb_link) { /*設置其雙親結點的首地址(根結點的雙親結點爲NULL),此結點顏色默認爲紅色*/ node->rb_parent_color = (unsigned long )parent; node->rb_left = node->rb_right = NULL; /*rb_link一般在調用前,就說明了是parent的左葉還是右葉 */ *rb_link = node; } |
Rbtree.c文件中函數
當在紅黑樹上進行插入和刪除操作時,對樹做了修改,結果可能會違反紅黑樹的那五條性質。爲了保持這些性質,就要改變樹中某些結點的顏色和指針結構。
指針結構的修改時通過旋轉來完成的,這是一種能保持二叉查找樹性質的查找樹局部操作:即左旋(右子爲軸,當前結點左旋)和右旋(左子爲軸,當前結點右旋:
左旋:比如說,需要把x旋轉爲y的左結點。整個算法的思路非常清晰:從上至下,先得到y指針,將x的右指針指向y的左結點,然後利用parent函數得到x的父親結點,如果爲NULL,則y爲新的根,如果不爲NULL,則根據x是其父親的左孩子還是右孩子,將指針指向y。最後將y的左指針指向x,完成旋轉。值得注意的是,算法是具有順序的邏輯步驟,不能夠調換順序,如果改變賦值的順序會造成內存失去指針指向,出現內存錯誤。
個人理解:假如X左旋,就是把X的右孩子Y作爲X的父節點,且X是Y的左孩子,Y原來的左孩子成爲X的右孩子,填補Y的空缺。如果是X右旋的話,就是把X的左孩子Y作爲X的父節點,且X是Y的右孩子,Y原來的右孩子成爲X的左孩子。更簡潔明瞭一點左旋就是交換右子樹和父節點,右旋就是交換左子樹和父節點。
__rb_rotate_left()
/*結點左旋操作,右子爲軸,當前結點左旋,node爲進行左旋操作的結點,旋轉前後對比上圖*/ static void __rb_rotate_left(struct rb_node *node, struct rb_root *root) { /*先獲得待左旋結點node的右子結點right和雙親節點parent*/ struct rb_node *right = node->rb_right; struct rb_node *parent = rb_parent(node);
/*將right的左子節點轉爲node的右子結點*/ if ((node->rb_right = right->rb_left)) rb_set_parent(right->rb_left, node); /*將node作爲right的左子結點*/ right->rb_left = node;
/*將node的雙親節點parent修改爲right的雙親節點*/ rb_set_parent(right, parent);
/*如果雙親結點不爲空,表示node不是根結點,用right取代node在parent子節點的位置,否則node原來就是根結點,將right設置爲根結點*/ if (parent) { if (node == parent->rb_left) parent->rb_left = right; else parent->rb_right = right; } else root->rb_node = right;
/*設置right爲node的雙親結點*/ rb_set_parent(node, right); } |
__rb_rotate_right()
/*結點右旋操作,左子爲軸,當前結點右旋,node爲進行右旋操作的結點,旋轉前後對比上圖*/ static void __rb_rotate_right(struct rb_node *node, struct rb_root *root) { /*先獲得待左旋結點node的左子結點left和雙親節點parent*/ struct rb_node *left = node->rb_left; struct rb_node *parent = rb_parent(node);
/*將left的右子節點轉爲node的左子結點*/ if ((node->rb_left = left->rb_right)) rb_set_parent(left->rb_right, node);
*將node作爲left的右子結點*/ left->rb_right = node;
/*將node的雙親節點parent修改爲right的雙親節點*/ rb_set_parent(left, parent);
/*如果雙親結點不爲空,表示node不是根結點,用left取代node在parent子節點的位置,否則node原來就是根結點,將left設置爲根結點*/ if (parent) { if (node == parent->rb_right) parent->rb_right = left; else parent->rb_left = left; } else root->rb_node = left; /*設置left爲node的雙親結點*/ rb_set_parent(node, left); }
|
rb_insert_color()
什麼時候用左旋,什麼時候用右旋?
當有不滿足紅黑樹特性的情況出現,且無法通過塗改顏色之類的操作來滿足(叔、父節點顏色不同,具體的話就是叔節點是黑的,父節點是紅的,必然是這樣的)。那麼祖父節點(必然是黑節點)就需要旋轉。右邊比左邊多,就需要右轉,左邊比右邊多就需要左旋。
下圖所示,G的右路黑路徑(2)比左路(1)高,G需要右旋。如果G要進行右旋,但是導致失衡的N、P跟G不在同一直線上。(不在一條直線上,旋轉會帶走插入的紅點N,下一步變顏色的時候會導致新一輪的失衡),所以先要搞成一條直線,N是P的右節點,所以P要先來一個左旋,如下圖一。
圖(一)
圖一中已經左旋完畢了,這時候要進行G的右旋,先交換N、P指針。但是由於N、P都是紅色,且P是要被拉上去的,G是要被拉下來的,所以可以先把P設置成黑色,G設置成紅色,再進行右旋。如圖(二)
圖(二)
/*;該函數與rb_link_node()配套使用,rb_link_node()爲結點插入函數,首先像二叉查找樹一樣插入一個新結點,然後rb_insert_color()函數根據情況作出相應的調整,以使其滿足紅黑樹的顏色屬性*/ void rb_insert_color(struct rb_node *node, struct rb_root *root) { struct rb_node *parent, *gparent;
/*如果插入的是根結點,直接修改其顏色屬性爲黑色,rb_link_node將結點插入時顏色屬性默認爲紅色; 如果插入結點的父結點爲黑色,那麼插入後紅黑樹顏色特性依舊是有效的,因爲插入的結點默認爲紅色,不會改變null上經過的黑色結點數目,也不會違背連續兩個紅色結點特性,所以不需要做任何修改; 否則需要判斷調整,使用while循環,判斷雙親結點是否存在以及顏色屬性是否爲紅色*/ while ((parent = rb_parent(node)) && rb_is_red(parent)) { /*獲取node的祖父節點 gparent, parent爲node的雙親結點*/ gparent = rb_parent(parent);
if (parent == gparent->rb_left) { /*獲取node的叔結點*/ register struct rb_node *uncle = gparent->rb_right;
/*如果node的雙親結點和叔結點都爲紅色,那麼他們都將變成黑色,祖父節點將變成紅色(這是爲了滿足一個結點到Null路徑上所有黑結點數目一致),但是如果這祖父節點變成root節點了,又要滿足root結點要是黑色,所以這個祖父節點還需要進行遞歸的判斷*/ if (uncle && rb_is_red(uncle)) { rb_set_black(uncle); rb_set_black(parent); rb_set_red(gparent); node = gparent; continue; } /*如果父節點是紅色的,但是叔節點是黑色的,插入的節點是父節點的右孩子,父節點又是祖父節點的左孩子。那麼就父節點進行左旋操作並且切換當前的節點和父節點(目的是將N、P、G變爲一條直線上),如上圖一左變成圖二左部分*/ if (parent->rb_right == node) { register struct rb_node *tmp; __rb_rotate_left(parent, root); tmp = parent; parent = node; node = tmp; } /*此時,父節點是紅色的,但是叔節點是黑色,節點是父節點的左節點,父節點是祖父節點的左節點,那麼祖父節點進行右旋操作。如圖二所示,P是N和G的父節點,G已知是黑色的,這時候不滿足紅節點的葉子節爲i黑色,所以要將祖父節點顏色塗紅,父節點顏色塗黑*/ rb_set_black(parent); rb_set_red(gparent); __rb_rotate_right(gparent, root); } else //即父結點爲祖父結點的右子結點 { register struct rb_node *uncle = gparent->rb_left;
/*如果node的雙親結點和叔結點都爲紅色,那麼他們都將變成黑色,祖父節點將變成紅色(這是爲了滿足一個結點到Null路徑上所有黑結點數目一致),但是如果這祖父節點變成root節點了,又要滿足root結點要是黑色,所以這個祖父節點還需要進行遞歸的判斷*/ if (uncle && rb_is_red(uncle)) { rb_set_black(uncle); rb_set_black(parent); rb_set_red(gparent); node = gparent; continue; }
/*如果父節點是紅色的,但是叔節點是黑色的,插入的節點是父節點的左孩子,父節點又是祖父節點的右孩子。那麼就父節點進行右旋操作並且切換當前的節點和父節點(目的是將N、P、G變爲一條直線上)*/ if (parent->rb_left == node) { register struct rb_node *tmp; __rb_rotate_right(parent, root); tmp = parent; parent = node; node = tmp; } /*此時,父節點是紅色的,但是叔節點是黑色,節點是父節點的右子節點,父節點是祖父節點的右子節點,那麼祖父節點進行左旋操作。這時候不滿足紅節點的葉子節爲i黑色,所以要將祖父節點顏色塗紅,父節點顏色塗黑*/ rb_set_black(parent); rb_set_red(gparent); __rb_rotate_left(gparent, root); } }
/*將根結點設置爲黑色*/ rb_set_black(root->rb_node); } |
__rb_erase_color()
在刪除過程中,如果遇到node的顏色爲黑便會違背紅黑樹的特性,因此需要進行調節,傳入的參數爲child、parent,即待刪節點中序遍歷直接後驅節點的子節點和父節點,因爲這個時候改刪的節點已經刪掉了!
第一種情況:刪除的結點爲根結點,而它的一個紅色孩子成爲了根,這裏只需要將結點改成黑色即可;
第二種情況:如上圖左所示,刪除了B結點右子樹上的一個黑結點後,被刪除結點的子結點爲紅色,BD紅色相連,並且B左子樹黑高度比右子樹高1,此時只需要將D染成黑色即可,即上圖右;
針對於這兩種情況的判斷就是在while的條件中,如果屬於這兩種情況,並且node不爲空就執行rb_set_black(node);
第三種情況,刪除黑色結點後,導致包含閃出結點的所有路徑上的黑高度少1,即刪除節點以及child都爲黑的情況,此時又跟child的兄弟節點相關了,函數中child用node表示,下面也都改爲node,又分爲如下幾種情況;
case 1:node的兄弟other是紅色,這種情況下,other的左右子結點都是黑色的,如下圖左所示。此時只需要將A、C顏色互相,並且parent進行左旋操作,此時node結點B又獲得了新的兄弟結點D,如下圖右所示。但是此時B子樹的黑高度仍然比D和E少1,但是這進入了一個新的case(見下case 2和case 3),即兄弟結點uncle爲黑的情況‘
case 2:node的兄弟other是黑色,且other的左右孩子都是黑色;如下圖左所示;此時的操作很簡單,將C變爲紅色,然後將node指向A,然後重新獲取A的雙親結點爲parent,即下圖右所示。由於刪除了一個黑節點,所以B子樹的黑高度會比C的黑高度少1,這裏將C設置爲紅色就是爲了給C子樹的黑高度減1,但是在這裏會讓整個以A爲根的子樹黑高度減1,不過沒關係,跳出循環之後將A設置爲黑色,這樣就會使之滿足紅黑樹性質。當然這個A不一定是紅色,如果是黑色,也就是下一次循環的時候node還是黑色,那就進入循環,繼續根據node的具體情況進行判斷,這是一個迭代的過程,最終的情況就是迭代到其它的情況,因爲即使運氣很差也會最終向上迭代到樹根。
case 3:node的兄弟other是黑色,且other的左孩子是紅色,右孩子爲黑色;如下圖左所示,
此時先將C和D的顏色互換,然後C結點右旋,結果如下圖右所示。通過圖中的操作其實是將case3轉換成case4,此時A右子樹黑深度仍然比左子樹的深度高1。
case 4:node的兄弟other是黑色,且other的右孩子是紅色,如下圖左所示;此時將C的顏色設置成A的顏色,然後將A以及E設置成黑色,然後A進行左旋,結果如下圖右所示。如果A剛開始就是黑色,無非就是右圖中C節點爲黑色,還是滿足性質。對下圖左這樣處理的原因有兩個:(1)要增加B子樹的黑高度;(2)要保持A右子樹的黑高度不變。因此,如果parent本身就是黑色,那麼實際只需要parent進行左旋,將E改爲黑色即可;如果parent爲紅色,爲了給B子樹增加黑高度,parent需要左旋和變黑,爲了不讓整個子樹黑高度增加,就需要將C變紅,然後E又需要變黑。所以將C改爲A的顏色最爲方便。
/*刪除的結點爲黑色時,需要對紅黑樹進行進一步調整,在node與parent之前,剛剛刪除了一個黑色節點。現在樹很可能不平衡, node與parent也可能紅色衝突。本函數進行樹的性質的修正,以使樹恢復平衡。在一些情況下問題會轉移到上一層節點,則須對上一層節點進行遞歸檢查與修正。本函數中的while循環實際上實現了這種遞歸。*/ static void __rb_erase_color(struct rb_node *node, struct rb_node *parent, struct rb_root *root) { /* other用來保存兄弟節點,這是樹的修正過程中一個重要的參考節點*/ struct rb_node *other;
/*循環條件:node不是紅節點且node不是根節點。 解釋:對於紅節點或根節點,直接塗黑即可解決問題*/ while ((!node || rb_is_black(node)) && node != root->rb_node) { /*當node爲parent左葉子結點的情況*/ if (parent->rb_left == node) { /*獲得兄弟結點*/ other = parent->rb_right; if (rb_is_red(other)) { /*對應case 1的情況,解釋見上*/ rb_set_black(other); rb_set_red(parent); __rb_rotate_left(parent, root); other = parent->rb_right; } if ((!other->rb_left || rb_is_black(other->rb_left)) && (!other->rb_right || rb_is_black(other->rb_right))) { /*對應case 2的情況,解釋見上*/ rb_set_red(other); node = parent; parent = rb_parent(node); } else { if (!other->rb_right || rb_is_black(other->rb_right)) { /*對應case 3的情況,解釋見上*/ rb_set_black(other->rb_left); rb_set_red(other); __rb_rotate_right(other, root); other = parent->rb_right; } /*對應case 4的情況,解釋見上*/ rb_set_color(other, rb_color(parent)); rb_set_black(parent); rb_set_black(other->rb_right); __rb_rotate_left(parent, root); node = root->rb_node; break; } } else { other = parent->rb_left; if (rb_is_red(other)) { rb_set_black(other); rb_set_red(parent); __rb_rotate_right(parent, root); other = parent->rb_left; } if ((!other->rb_left || rb_is_black(other->rb_left)) && (!other->rb_right || rb_is_black(other->rb_right))) { rb_set_red(other); node = parent; parent = rb_parent(node); } else { if (!other->rb_left || rb_is_black(other->rb_left)) { rb_set_black(other->rb_right); rb_set_red(other); __rb_rotate_left(other, root); other = parent->rb_left; } rb_set_color(other, rb_color(parent)); rb_set_black(parent); rb_set_black(other->rb_left); __rb_rotate_right(parent, root); node = root->rb_node; break; } } } if (node) rb_set_black(node); } |
rb_erase()
像二叉查找樹的刪除操作一樣,首先需要找到所需刪除的結點,然後根據該結點左右子樹的有無分爲三種情形:
若node結點的顏色屬性爲黑色,則需要調用__rb_erase_color函數來進行調整。
結合紅黑樹的定義分析,如果刪除紅色節點會帶來什麼影響:
a:樹中的黑節點高度仍保持不變
b:不存在兩個相鄰的紅節點
c:如果刪除的是紅色節點,那麼該節點肯定不是根節點
剩下的兩條肯定滿足,這樣看來如果刪除了紅色節點對紅黑樹並沒有任何影響。
那麼,如果刪除了黑色節點呢:
a:如果node是根節點,而它的一個紅色孩子成爲了根,違反(2)
b:如果刪除node後,node的父節點與其新的子節點同爲紅色,違反(4)
c:刪除node後將導致包含node的所有路徑上的黑高度少1,違反(5)
其它滿足。
從上面的分析可以看出來刪除了黑節點纔會真正影響紅黑樹的性質。
/*紅黑樹結點刪除函數*/ void rb_erase(struct rb_node *node, struct rb_root *root) { struct rb_node *child, *parent; int color;
/*尋找刪除node後與parent相連的child: 如果node無左子結點,那麼child便爲其右子結點; 如果node無右子結點,那麼child便爲其左子結點; 如果node同時有左右子結點,用node右子結點的最左子樹結點來取代node*/ if (!node->rb_left) child = node->rb_right; else if (!node->rb_right) child = node->rb_left; else { /*old指向待刪除的結點*/ struct rb_node *old = node, *left;
/*node指向將要取代old位置的結點,即old右子結點的最左子樹結點(old的中序遍歷直接後驅節點)*/ node = node->rb_right; while ((left = node->rb_left) != NULL) node = left;
/*將新node加到old雙親結點對應子結點,取代old,注意此時node的parent地址還是原來的*/ if (rb_parent(old)) { if (rb_parent(old)->rb_left == old) rb_parent(old)->rb_left = node; else rb_parent(old)->rb_right = node; } else root->rb_node = node;
/*node的右子樹改爲其原parent的左子樹*/ child = node->rb_right; parent = rb_parent(node); color = rb_color(node);
if (parent == old) { parent = node; } else { if (child) rb_set_parent(child, parent); parent->rb_left = child; /*將old的右子樹改爲node的右子樹*/ node->rb_right = old->rb_right; rb_set_parent(old->rb_right, node); } /*此時才修改node的雙親地址,注意的是,此時node的顏色也變成了和old一致,那麼node原來層次以上的結構都不會有問題,只有在node原來顏色爲黑色時,node及以下子樹經過調整纔會違背紅黑樹的特性,所以上面纔會color = rb_color(node);並在後面判斷color屬性*/ node->rb_parent_color = old->rb_parent_color; /*將old的左子樹改爲node的左子樹* node->rb_left = old->rb_left; rb_set_parent(old->rb_left, node);
goto color; /*在存在左右子樹的情況中,從刪除前後紅黑樹結構變化來看,最終實際是刪除了node,只是將其挪到了old位置,然後將其顏色也改爲node的顏色……*/ } /*對於缺少左子樹或者右子樹的node,直接建立其child與parent的連接*/ parent = rb_parent(node); color = rb_color(node);
if (child) rb_set_parent(child, parent); if (parent) { if (parent->rb_left == node) parent->rb_left = child; else parent->rb_right = child; } else root->rb_node = child; /*如果刪除結點的顏色屬性爲黑色,則需調用__rb_erase_color函數來進行調整*/ color: if (color == RB_BLACK) __rb_erase_color(child, parent, root); } |
rb_first()
/*返回紅黑樹中最左結點,即排序最小的結點 */ struct rb_node *rb_first(const struct rb_root *root) { struct rb_node *n;
n = root->rb_node; if (!n) return NULL; while (n->rb_left) n = n->rb_left; return n; } |
rb_last()
/*返回紅黑樹中最右結點,即排序最大的結點 */ struct rb_node *rb_last(const struct rb_root *root) { struct rb_node *n;
n = root->rb_node; if (!n) return NULL; while (n->rb_right) n = n->rb_right; return n; } |
rb_next()
/*找node中序遍歷的直接後驅結點,即排序在node後一個的結點*/ struct rb_node *rb_next(const struct rb_node *node) { struct rb_node *parent;
if (rb_parent(node) == node) return NULL;
/*如果node有右分支,那麼其右分支的最左節點即爲所需結點*/ if (node->rb_right) { node = node->rb_right; while (node->rb_left) node=node->rb_left; return (struct rb_node *)node; } /*當node不存在右分支時,沿父節點向上遍歷,找到第一個出現左分支的節點便是需要的節點*/ while ((parent = rb_parent(node)) && node == parent->rb_right) node = parent;
return parent; } |
rb_prev()
/*找node中序遍歷的直接前驅結點,即排序在node前一個的結點*/ struct rb_node *rb_prev(const struct rb_node *node) { struct rb_node *parent;
if (rb_parent(node) == node) return NULL;
if (node->rb_left) { node = node->rb_left; while (node->rb_right) node=node->rb_right; return (struct rb_node *)node; } while ((parent = rb_parent(node)) && node == parent->rb_left) node = parent;
return parent; }
|
rb_replace_node()
/*用new結點取代victim結點*/ void rb_replace_node(struct rb_node *victim, struct rb_node *new, struct rb_root *root) { struct rb_node *parent = rb_parent(victim);
if (parent) { if (victim == parent->rb_left) parent->rb_left = new; else parent->rb_right = new; } else { root->rb_node = new; } if (victim->rb_left) rb_set_parent(victim->rb_left, new); if (victim->rb_right) rb_set_parent(victim->rb_right, new);
*new = *victim; } |
紅黑樹的應用Rblist
Rblist.h文件中數據結構及函數
rblist
struct rblist { struct rb_root entries; //liat的根結點 unsigned int nr_entries; //list中結點的數目
/*比較函數,返回值=value(rbn) - value(entry)*/ int (*node_cmp)(struct rb_node *rbn, const void *entry);
/*爲新結點創建紅黑樹結點結構*/ struct rb_node *(*node_new)(struct rblist *rlist, const void *new_entry);
/*釋放紅黑樹中指定結點結構空間*/ void (*node_delete)(struct rblist *rblist, struct rb_node *rb_node); }; |
rblist__empty()
/*判斷樹是否爲空*/ static inline bool rblist__empty(const struct rblist *rblist) { return rblist->nr_entries == 0; } |
rblist__nr_entries()
/*獲取樹中結點的數目*/ static inline unsigned int rblist__nr_entries(const struct rblist *rblist) { return rblist->nr_entries; } |
Rblist.c文件中函數
rblist__add_node()
/*結點插入紅黑樹中適當位置*/ int rblist__add_node(struct rblist *rblist, const void *new_entry) { /*獲取根結點*/ struct rb_node **p = &rblist->entries.rb_node; struct rb_node *parent = NULL, *new_node;
/*根據new_entry的值找到合適的插入位置*/ while (*p != NULL) { int rc; parent = *p; /*比較,若parent大,則往左子樹,若parent小則往右子樹*/ rc = rblist->node_cmp(parent, new_entry); if (rc > 0) p = &(*p)->rb_left; else if (rc < 0) p = &(*p)->rb_right; else return -EEXIST; } /*爲新結點創建紅黑樹結點結構*/ new_node = rblist->node_new(rblist, new_entry); if (new_node == NULL) return -ENOMEM; /*將結點連入紅黑樹中,併爲其着色*/ rb_link_node(new_node, parent, p); rb_insert_color(new_node, &rblist->entries); /*紅黑樹結點數目+1*/ ++rblist->nr_entries;
return 0; }
|
rblist__remove_node()
/*刪除結點,並釋放其空間(爲何rblist->nr_entries沒有執行減一操作?)*/ void rblist__remove_node(struct rblist *rblist, struct rb_node *rb_node) { rb_erase(rb_node, &rblist->entries); rblist->node_delete(rblist, rb_node); } |
rblist__find()
/*紅黑樹中查找指定結點*/ struct rb_node *rblist__find(struct rblist *rblist, const void *entry) { struct rb_node **p = &rblist->entries.rb_node; struct rb_node *parent = NULL;
while (*p != NULL) { int rc; parent = *p; rc = rblist->node_cmp(parent, entry); if (rc > 0) p = &(*p)->rb_left; else if (rc < 0) p = &(*p)->rb_right; else return parent; } return NULL; } |
rblist__init()
/*紅黑樹結構初始化*/ void rblist__init(struct rblist *rblist) { if (rblist != NULL) { rblist->entries = RB_ROOT; rblist->nr_entries = 0; } return; } |
rblist__delete()
/*按照中序遍歷的方式刪除紅黑樹中所有結點,並釋放其空間*/ void rblist__delete(struct rblist *rblist) { if (rblist != NULL) { struct rb_node *pos, *next = rb_first(&rblist->entries);
while (next) { pos = next; next = rb_next(pos); rb_erase(pos, &rblist->entries); rblist->node_delete(rblist, pos); } free(rblist); } } |
rblist__entry()
/*查找紅黑樹中從小到大排序的第idx個結點,即中序遍歷的第idx個結點*/ struct rb_node *rblist__entry(const struct rblist *rblist, unsigned int idx) { struct rb_node *node;
for (node = rb_first(&rblist->entries); node; node = rb_next(node)) { if (!idx--) return node; } return NULL; } |