紅黑樹詳解(源碼+圖示)

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的父節點,且XY的左孩子,Y原來的左孩子成爲X的右孩子,填補Y的空缺。如果是X右旋的話,就是把X的左孩子Y作爲X的父節點,且XY的右孩子,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取代nodeparent子節點的位置,否則node原來就是根結點,將right設置爲根結點*/

if (parent)

{

if (node == parent->rb_left)

parent->rb_left = right;

else

parent->rb_right = right;

}

else

root->rb_node = right;

 

    /*設置rightnode的雙親結點*/

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取代nodeparent子節點的位置,否則node原來就是根結點,將left設置爲根結點*/

if (parent)

{

if (node == parent->rb_right)

parent->rb_right = left;

else

parent->rb_left = left;

}

else

root->rb_node = left;

    /*設置leftnode的雙親結點*/

       rb_set_parent(node, left);

}

 

rb_insert_color()

什麼時候用左旋,什麼時候用右旋?

    當有不滿足紅黑樹特性的情況出現,且無法通過塗改顏色之類的操作來滿足(叔、父節點顏色不同,具體的話就是叔節點是黑的,父節點是紅的,必然是這樣的)。那麼祖父節點(必然是黑節點)就需要旋轉。右邊比左邊多,就需要右轉,左邊比右邊多就需要左旋。

    下圖所示,G的右路黑路徑(2)比左路(1)高,G需要右旋。如果G要進行右旋,但是導致失衡的NPG不在同一直線上。(不在一條直線上,旋轉會帶走插入的紅點N,下一步變顏色的時候會導致新一輪的失衡),所以先要搞成一條直線,NP的右節點,所以P要先來一個左旋,如下圖一。

 

()

圖一中已經左旋完畢了,這時候要進行G的右旋,先交換NP指針。但是由於NP都是紅色,且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, parentnode的雙親結點*/

        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;

}

    /*如果父節點是紅色的,但是叔節點是黑色的,插入的節點是父節點的右孩子,父節點又是祖父節點的左孩子。那麼就父節點進行左旋操作並且切換當前的節點和父節點(目的是將NPG變爲一條直線上),如上圖一左變成圖二左部分*/

if (parent->rb_right == node)

{

register struct rb_node *tmp;

__rb_rotate_left(parent, root);

tmp = parent;

parent = node;

node = tmp;

}

    /*此時,父節點是紅色的,但是叔節點是黑色,節點是父節點的左節點,父節點是祖父節點的左節點,那麼祖父節點進行右旋操作。如圖二所示,PNG的父節點,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;

}

 

    /*如果父節點是紅色的,但是叔節點是黑色的,插入的節點是父節點的左孩子,父節點又是祖父節點的右孩子。那麼就父節點進行右旋操作並且切換當前的節點和父節點(目的是將NPG變爲一條直線上)*/

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的顏色爲黑便會違背紅黑樹的特性,因此需要進行調節,傳入的參數爲childparent,即待刪節點中序遍歷直接後驅節點的子節點和父節點,因爲這個時候改刪的節點已經刪掉了!

     

  

第一種情況:刪除的結點爲根結點,而它的一個紅色孩子成爲了根,這裏只需要將結點改成黑色即可;

第二種情況:如上圖左所示,刪除了B結點右子樹上的一個黑結點後,被刪除結點的子結點爲紅色,BD紅色相連,並且B左子樹黑高度比右子樹高1,此時只需要將D染成黑色即可,即上圖右;

針對於這兩種情況的判斷就是在while的條件中,如果屬於這兩種情況,並且node不爲空就執行rb_set_black(node)

第三種情況,刪除黑色結點後,導致包含閃出結點的所有路徑上的黑高度少1,即刪除節點以及child都爲黑的情況,此時又跟child的兄弟節點相關了,函數中childnode表示,下面也都改爲node,又分爲如下幾種情況;

case 1node的兄弟other是紅色,這種情況下,other的左右子結點都是黑色的,如下圖左所示。此時只需要將AC顏色互相,並且parent進行左旋操作,此時node結點B又獲得了新的兄弟結點D,如下圖右所示。但是此時B子樹的黑高度仍然比DE1,但是這進入了一個新的case(見下case 2case 3),即兄弟結點uncle爲黑的情況‘

  

  case 2node的兄弟other是黑色,且other的左右孩子都是黑色;如下圖左所示;此時的操作很簡單,將C變爲紅色,然後將node指向A,然後重新獲取A的雙親結點爲parent,即下圖右所示。由於刪除了一個黑節點,所以B子樹的黑高度會比C的黑高度少1,這裏將C設置爲紅色就是爲了給C子樹的黑高度減1,但是在這裏會讓整個以A爲根的子樹黑高度減1,不過沒關係,跳出循環之後將A設置爲黑色,這樣就會使之滿足紅黑樹性質。當然這個A不一定是紅色,如果是黑色,也就是下一次循環的時候node還是黑色,那就進入循環,繼續根據node的具體情況進行判斷,這是一個迭代的過程,最終的情況就是迭代到其它的情況,因爲即使運氣很差也會最終向上迭代到樹根。

 

 

case 3node的兄弟other是黑色,且other的左孩子是紅色,右孩子爲黑色;如下圖左所示,

此時先將CD的顏色互換,然後C結點右旋,結果如下圖右所示。通過圖中的操作其實是將case3轉換成case4,此時A右子樹黑深度仍然比左子樹的深度高1

 

 

case 4node的兄弟other是黑色,且other的右孩子是紅色,如下圖左所示;此時將C的顏色設置成A的顏色,然後將A以及E設置成黑色,然後A進行左旋,結果如下圖右所示。如果A剛開始就是黑色,無非就是右圖中C節點爲黑色,還是滿足性質。對下圖左這樣處理的原因有兩個:(1)要增加B子樹的黑高度;(2)要保持A右子樹的黑高度不變。因此,如果parent本身就是黑色,那麼實際只需要parent進行左旋,將E改爲黑色即可;如果parent爲紅色,爲了給B子樹增加黑高度,parent需要左旋和變黑,爲了不讓整個子樹黑高度增加,就需要將C變紅,然後E又需要變黑。所以將C改爲A的顏色最爲方便。

  

 

/*刪除的結點爲黑色時,需要對紅黑樹進行進一步調整,在nodeparent之前,剛剛刪除了一個黑色節點。現在樹很可能不平衡, nodeparent也可能紅色衝突。本函數進行樹的性質的修正,以使樹恢復平衡。在一些情況下問題會轉移到上一層節點,則須對上一層節點進行遞歸檢查與修正。本函數中的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)

{

/*nodeparent左葉子結點的情況*/

        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,注意此時nodeparent地址還是原來的*/

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,直接建立其childparent的連接*/

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;

}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章