紅黑樹(附完整C代碼)

分類:IT技術 時間:2016-10-10

版權聲明:原創不易,轉載請註明轉自weewqrer 紅黑樹


紅黑樹簡介

首先紅黑樹是一棵二叉搜索樹,它在每個結點上增加了一個存儲位來表示結點的顏色,可以是RED或者BLACK。通過對一條從根節點到NIL葉節點(指空結點或者下面說的哨兵)的簡單路徑上各個結點在顏色進行約束,紅黑樹確保沒有一條路徑會比其他路徑長出2倍,因而是近似平衡的。
此處輸入圖片的描述

用途

紅黑樹和AVL樹一樣都對插入時間、刪除時間和查找時間提供了最好可能的最壞情況擔保。對於查找、插入、刪除、最大、最小等動態操作的時間復雜度為O(lgn).常見的用途有以下幾種:

  • STL(標準模板庫)中在set map是基於紅黑樹實現的。
  • Java中在TreeMap使用的也是紅黑樹。
  • epoll在內核中的實現,用紅黑樹管理事件塊。
  • linux進程調度Completely Fair Scheduler,用紅黑樹管理進程控制塊

紅黑樹 VS AVL樹

常見的平衡樹有紅黑樹和AVL平衡樹,為什麽STL和linux都使用紅黑樹作為平衡樹的實現?大概有以下幾個原因:

  1. 從實現細節上來講,如果插入一個結點引起了樹的不平衡,AVL樹和紅黑樹都最多需要2次旋轉操作,即兩者都是O(1);但是在刪除node引起樹的不平衡時,最壞情況下,AVL需要維護從被刪node到root這條路徑上所有node的平衡性,因此需要旋轉的量級O(logN),而RB-Tree最多只需3次旋轉,只需要O(1)的復雜度

  2. 從兩種平衡樹對平衡的要求來講,AVL的結構相較RB-Tree來說更為平衡,在插入和刪除node更容易引起Tree的unbalance,因此在大量數據需要插入或者刪除時,AVL需要rebalance的頻率會更高。因此,RB-Tree在需要大量插入和刪除node的場景下,效率更高。自然,由於AVL高度平衡,因此AVL的search效率更高。

  3. 總體來說,RB-tree的統計性能是高於AVL的。

關於參考書

《算法導論》和《數據結構與算法分析》是大家常用的兩本算法書,針對紅黑樹這一章,這兩本書上也都有,但是二者從數據結構到使用的方法上都不一樣,這裏我推薦使用《算法導論》。有以下幾個原因:

  • 《數據結構與算法分析》中使用的數據結構沒有保存父親結點,所以在調用旋轉函數時需要用函數返回值來保持上下結點的連接,這在avl樹中顯得很簡潔,因為在avl中的情況比較簡單,但是在紅黑樹中涉及到了祖祖父、祖父、父親、兒子、四代結點,按照這本書上的方法得保存GGP、GP、P、X四個全局變量的值,在更新它們的指向時非常容易搞混。
  • 《數據結構與算法分析》沒有實現刪除操作,只是描述了實現的方法,按照這上面的方法我也做了實現,最後發現代碼非常長,至少150+,不如《算法導論》中的簡潔。
  • 另外,《算法導論》中描述的方法比較完整,思路嚴謹。

紅黑樹詳解

紅黑樹性質

紅黑樹是每個結點都帶有顏色屬性的二叉查找樹,顏色為紅色或黑色。在二叉查找樹強制一般要求以外,對於任何有效的紅黑樹我們增加了如下的額外要求:

  1. 列表項結點是紅色或黑色。
  2. 根是黑色。
  3. 所有葉子都是黑色(葉子是NIL結點)。
  4. 每個紅色結點必須有兩個黑色的子結點。(從每個葉子到根的所有路徑上不能有兩個連續的紅色結點。)
  5. 從任一結點到其每個葉子的所有簡單路徑都包含相同數目的黑色結點。
    為了便於處理紅黑樹中的邊界情況,使用一個哨兵來代表所有的NIL結點,也就是說所有指向NIL的指針都指向哨兵T.nil。

紅黑樹數據結構

typedef enum ColorType {RED, BLACK} ColorType;
typedef struct rbt_t{
    int key;
    rbt_t * left;
    rbt_t * right;
    rbt_t * p;
    ColorType color;
}rbt_t;

typedef struct rbt_root_t{
    rbt_t* root;
    rbt_t* nil;
}rbt_root_t;



/*
*@brief rbt_init 初始化
*/
rbt_root_t* rbt_init(void){
    rbt_root_t* T;

    T = (rbt_root_t*)malloc(sizeof(rbt_root_t));
    assert( NULL != T);

    //用一個哨兵代表NIL。
    T->nil = (rbt_t*)malloc(sizeof(rbt_t));
    assert(NULL != T->nil);
    T->nil->color = BLACK;
    T->nil->left = T->nil->right = NULL;
    T->nil->p = NULL;

    T->root = T->nil;
    return T;
}

紅黑樹旋轉

搜索樹操作inert和delete在含有n個關鍵字的紅黑樹上,運行花費時間為O(lgn)。由於這兩個操作對樹做了修改,所以有可能違反上面的性質,所以需要改變樹中某些結點的顏色以及指針的結構。

指針結構的修改是通過旋轉來完成的,這是一種能保持二叉搜索樹性質的局部操作。一種有兩種旋轉操作,如下圖所示,都在O(1)時間內完成。

這裏要求x,y都不是T.nil。
這裏寫圖片描述

c代碼:

/*
*@brief rbt_left_rotate
*@param[in] T 樹根
*@param[in] x 要進行旋轉的節點
*/
void rbt_left_rotate( rbt_root_t* T, rbt_t* x){
    rbt_t* y = x->right;

    x->right = y->left;
    if(x->right != T->nil)//更新某結點的父親時,要確定此結點不是T.nil
        x->right->p = x;

    y->p = x->p;
    if(x->p == T->nil){//如果x以前是樹根,那麽現在樹根易主了
        T->root = y;
    }else if(y->key < y->p->key)
        y->p->left = y;
    else
        y->p->right = y;

    y->left = x;
    x->p = y;
}
/*
*@brief rbt_right_rotate
*@param[in] 樹根
*@param[in] 要進行旋轉的節點
*/
void rbt_right_rotate( rbt_root_t* T, rbt_t* x){
    rbt_t * y = x->left;
    x->left = y->right;

    if(T->nil != x->left)
        x->left->p = x;

    y->p = x->p;
    if(y->p == T->nil)
        T->root = y;
    else if(y->key < y->p->key)
        y->p->left= y;
    else
        y->p->right = y;

    y->right = x;
    x->p = y;
}

紅黑樹插入

在紅黑樹中插入一個元素,跟在二叉搜索樹中插入一個元素一樣,只是插入一個元素之後,有可能使得這個樹不再平衡,所以要再處理一下,使之重新回到平衡狀態。

圖中N為新插入的結點,U為它的叔叔。
這裏寫圖片描述
插入操作的關鍵在於以下幾點:

  1. 新插入的節點一定是紅色的。(如果是黑色的,會破壞條件5)
  2. 如果新插入的節點的父親是黑色的,則沒有破壞任何性質,那麽插入完成。
  3. 如果插入節點的父節點是紅色, 破壞了性質4. 故插入算法就是通過重新著色或旋轉, 來維持性質

插入一個紅色節點要處理這麽幾種情況:

此時要記住一件事事情,插入時總是要考慮它的叔叔,刪除時總要考慮它的兄弟。而且插入時維護的主要是顏色(性質4),而刪除時維護的主要是黑色結點數量(性質5)

情況1:

N為紅,P為紅(GP一定為黑),U為紅。
這裏寫圖片描述
下面會說明我們可以通過一種特殊的處理把這種情況避免掉。

那為什麽要避免這種情況呢?因為這種情況一般是通過顏色翻轉來處理的,也就是把P U換成黑色,把GP抱成紅色,但是GP的父親如果是紅色的話又會違反紅黑樹的性質。

情況2:

N,P都為紅(GP一定為黑),U為黑
這裏寫圖片描述

根據境像,情況2可細分為4種情況,如下:
這裏寫圖片描述
但是這四種具體情況的處理手法是一樣的,都是通過顏色翻轉與旋轉來處理的。下面我們通過情況2.1和2.2來說明一下處理方法:
情況2.2通過調用left_rotate(T,p)變成情況2.1;
情況2.1通過交換GP與P的顏色,然後調用right_rotate(T,GP),此時不再違反任何性質。

情況2.3和2.4分別是2.1和2.2的境像。

如何避免情況1

令X = T.root,在向下遍歷的過程中,我們如果遇到X.right.color == x.left.color == RED時我們將x與它孩子的顏色翻轉,即把x塗成紅色,把x.right和x.left塗成黑色。

如果x的父親為黑色,沒有違反性質;如果x的父親為紅色,那麽可以把x當成新插入的紅色結點N,那麽只需要處理情況2即可。

至此,插入完成,具體實現可以看完整代碼部分,代碼也有必要的註釋。

紅黑樹刪除

這裏寫圖片描述
還是上面同樣那句話,插入時總是要考慮它的叔叔,刪除時總要考慮它的兄弟。而且插入時維護的主要是顏色(性質4),而刪除時維護的主要是黑色結點數量(性質5)。

寫刪除的代碼花費了我大概一天多的時間,因為我總是試圖找出一種比《數據結構與算法分析》上更清晰,比《算法導論》中更簡單的方法,但是失敗了(⊙▽⊙).

實際上刪除操作也沒有那麽難,如果要刪除 z 結點,那麽就讓 z 的後繼來代替 z 的位置即可。 如果z是紅色的,那麽操作便完成了,刪除一個紅色結點沒有違反任何性質。但如果z是黑色的,那麽我們刪除一個黑色結點,便違反了性質5,造成黑色結點數量的左右不平衡。只要分析出刪除一個黑色結點會遇到哪些情況即可。

首先找到要刪除的結點,我們定義它為 z 。

如果 z 的兩個孩子都不是T.nil,那麽我們在 z 的右子樹中找出最小的結點 m,把 m 結點的值賦給 z (而不是把m移植到z的位置,也就不用考慮顏色問題,這一點是比《算法導論》中要簡單的),那麽我們要刪除的結點就成為 m 了。m 肯定沒有左孩子。令 z 重新指向 m .

找到要刪除的結點 z 之後,我們用 z 的孩子(記作 x )來取代 z的位置(即使z.right == T.nil) 。

rbt_transplant(T,z,z.right);

此時用到下面一段代碼,實現用v代替u

void rbt_transplant(rbt_root_t* T, rbt_t* u, rbt_t* v){
    if(u->p == T->nil)
        T->root = v;
    else if(u == u->p->left)
        u->p->left =v;
    else
        u->p->right = v;
    v->p = u->p;//即使v是T.nil也可以執行這一行
}

到目前為止,如果要被刪除的 z 結點是紅色的,那麽程序就結束了。但是如果 z 是黑色的,所以刪除z之後z這邊少了一個黑色結點,會違反性質5,此時分為4種情況(x 是左孩子 和 x 是右孩子分別有4種情況,現在只討論x是左孩子的情況):

情況1

x的兄弟w是紅色的,那麽它們的父親、w的孩子都是黑色的。

這種情況下只能做一種無損的操作,通過交換顏色再旋轉,對樹的性質不會產生影響,所以從根到x結點的路徑上少的一個黑色結點也不會補上。

交換p與w的顏色,再對p進行左旋操之後,x的新兄弟就為黑色,情況變成了2 3 4中的一種.
這裏寫圖片描述
圖中x為白色,表示我們不關心x的顏色。

情況2

x的兄弟w是黑色,而且w的兩個孩子都是黑色。

此時可以細分為2種情況,但無論哪種情況,我們要進行的操作都是一樣的,都是將w塗成紅色,將p塗成黑色。

如果是情況2.1(有可能由情況1發展過來的),由於上述操作為x那邊補上了一個黑色(從根到x在路徑上多了一個黑色結點),此時紅黑樹性質5得到滿足,程序結束。

如果是情況2.2, 經過上述操作後,P的右子樹也少了一個黑色結點,令P作為新的X繼續循環。
這裏寫圖片描述

情況3

W是黑色有,w在左孩子是紅色的,W的右孩子是黑色的。

通過交換L與W的顏色,再對W進行右旋操作。這種操作也不會對紅黑樹性質產生影響,此時進入情況4,我們會看到通過情況4中的操作最終使紅黑樹性質得到滿足,結束程序。

圖中最後邊的R結點沒有畫出來,因為我們不關心它了
這裏寫圖片描述

情況4

w是黑色的,w的右孩子是紅色的。

把w塗成p的顏色,把P塗成黑色,R塗成黑色,左旋P。此時從根到x在路徑上多了一個黑色結點,程序結束。
這裏寫圖片描述

具體實現代碼見下面。

完整代碼(C)

#include<stdafx.h>
#include<malloc.h>
#include <assert.h>

//版權聲明:原創不易,轉載請註明轉自[weewqrer 紅黑樹](http://blog.csdn.net/weewqrer/article/details/51866488)

//紅黑樹
typedef enum ColorType {RED, BLACK} ColorType;
typedef struct rbt_t{
    int key;
    rbt_t * left;
    rbt_t * right;
    rbt_t * p;
    ColorType color;
}rbt_t;

typedef struct rbt_root_t{
    rbt_t* root;
    rbt_t* nil;
}rbt_root_t;

//函數聲明
rbt_root_t* rbt_init(void);
static void rbt_handleReorient(rbt_root_t* T, rbt_t* x, int k);
rbt_root_t* rbt_insert(rbt_root_t* &T, int k);
rbt_root_t* rbt_delete(rbt_root_t* &T, int k);

void rbt_transplant(rbt_root_t* T, rbt_t* u, rbt_t* v);

static void rbt_left_rotate( rbt_root_t* T, rbt_t* x);
static void rbt_right_rotate( rbt_root_t* T, rbt_t* x);

void rbt_inPrint(const rbt_root_t* T, rbt_t* t);
void rbt_prePrint(const rbt_t * T, rbt_t* t);
void rbt_print(const rbt_root_t* T);

static rbt_t* rbt_findMin(rbt_root_t * T, rbt_t* t);
static rbt_t* rbt_findMax(rbt_root_t * T, rbt_t* t);

static rbt_t* rbt_findMin(rbt_root_t * T, rbt_t* t){
    if(t == T->nil) return T->nil;

    while(t->left != T->nil)
        t = t->left;
    return t;
}
static rbt_t* rbt_findMax(rbt_root_t * T, rbt_t* t){
    if(t == T->nil) return T->nil;

    while(t->right != T->nil)
        t = t->right;
    return t;
}
/*
*@brief rbt_init 初始化
*/
rbt_root_t* rbt_init(void){
    rbt_root_t* T;

    T = (rbt_root_t*)malloc(sizeof(rbt_root_t));
    assert( NULL != T);

    T->nil = (rbt_t*)malloc(sizeof(rbt_t));
    assert(NULL != T->nil);
    T->nil->color = BLACK;
    T->nil->left = T->nil->right = NULL;
    T->nil->p = NULL;

    T->root = T->nil;

    return T;
}

/*
*@brief rbt_handleReorient  內部函數 由rbt_insert調用
*      在兩種情況下調用這個函數:
* 1 x有連個紅色兒子
* 2 x為新插入的結點
*
*/ 
void rbt_handleReorient(rbt_root_t* T, rbt_t* x, int k){

    //在第一種情況下,進行顏色翻轉; 在第二種情況下,相當於對新插入的x點初始化
    x->color = RED;
    x->left->color = x->right->color = BLACK;

    //如果x.p為紅色,那麽x.p一定不是根,x.p.p一定不是T.nil,而且為黑色
    if(  RED == x->p->color){
        x->p->p->color = RED;//此時x, p, x.p.p都為紅

        if(x->p->key < x->p->p->key){
            if(k > x->p->key){
                x->color = BLACK;//小心地處理顏色
                rbt_left_rotate(T,x->p);
                rbt_right_rotate(T,x->p);
            }else{
                x->p->color = BLACK;//小心地處理顏色
                rbt_right_rotate(T,x->p->p);
            }

        }else{
            if(k < x->p->key){
                x->color = BLACK;
                rbt_right_rotate(T,x->p);
                rbt_left_rotate(T,x->p);
            }else{
                x->p->color = BLACK;
                rbt_left_rotate(T,x->p->p);
            }

        }
    }

    T->root->color = BLACK;//無條件令根為黑色
}
/*
*@brief brt_insert 插入
*1 新插入的結點一定是紅色的,如果是黑色的,會破壞條件4(每個結點到null葉結點的每條路徑有同樣數目的黑色結點)
*2 如果新插入的結點的父親是黑色的,那麽插入完成。 如果父親是紅色的,那麽做一個旋轉即可。(前提是叔叔是黑色的)
*3 我們這個插入要保證其叔叔是黑色的。也就是在x下沈過程中,不允許存在兩個紅色結點肩並肩。
*/
rbt_root_t* rbt_insert(rbt_root_t* &T, int k){

    rbt_t * x, *p;
    x = T->root;
    p = x;

    //令x下沈到葉子上,而且保證一路上不會有同時為紅色的兄弟
    while( x != T->nil){        
        //
        //保證沒有一對兄弟同時為紅色, 為什麽要這麽做?
        if(x != T->nil)         
            if(x->left->color == RED && x->right->color == RED)
                rbt_handleReorient(T,x,k);

        p = x;
        if(k<x->key)
            x = x->left;
        else if(k>x->key)
            x = x->right;
        else{
            printf("\n%d已存在\n",k);
            return T;
        }

    }

    //為x分配空間,並對其進行初始化
    x = (rbt_t *)malloc(sizeof(rbt_t));
    assert(NULL != x);
    x->key = k;
    x->color = RED;
    x->left = x->right = T->nil;
    x->p = p;

    //讓x的父親指向x
    if(T->root == T->nil)
        T->root = x;        
    else if(k < p->key)
        p->left = x;
    else
        p->right = x;

    //因為一路下來,如果x的父親是紅色,那麽x的叔叔肯定不是紅色了,這個時候只需要做一下翻轉即可。
    rbt_handleReorient(T,x,k);

    return T;
}
void rbt_transplant(rbt_root_t* T, rbt_t* u, rbt_t* v){
    if(u->p == T->nil)
        T->root = v;
    else if(u == u->p->left)
        u->p->left =v;
    else
        u->p->right = v;
    v->p = u->p;
}
/*
*@brief rbt_delete 從樹中刪除 k
*
*
*/
rbt_root_t* rbt_delete(rbt_root_t* &T, int k){
    assert(T != NULL);
    if(NULL == T->root) return T;

    //找到要被刪除的葉子結點
    rbt_t * toDelete = T->root; 
    rbt_t * x;

    //找到值為k的結點
    while(toDelete != T->nil && toDelete->key != k){
        if(k<toDelete->key)
            toDelete = toDelete->left;
        else if(k>toDelete->key)
            toDelete = toDelete->right;
    }

    if(toDelete == T->nil){
        printf("\n%d 不存在\n",k);
        return T;
    }


    //如果兩個孩子,就找到右子樹中最小的代替, alternative最多有一個右孩子
    if(toDelete->left != T->nil && toDelete->right != T->nil){
        rbt_t* alternative = rbt_findMin(T, toDelete->right);
        k = toDelete->key = alternative->key;
        toDelete = alternative;
    }

    if(toDelete->left == T->nil){
        x = toDelete->right;
        rbt_transplant(T,toDelete,toDelete->right);
    }else if(toDelete->right == T->nil){
        x = toDelete->left;
        rbt_transplant(T,toDelete,toDelete->left);
    }



    if(toDelete->color == BLACK){
        //x不是todelete,而是用於代替x的那個
        //如果x顏色為紅色的,把x塗成黑色即可, 否則 從根到x處少了一個黑色結點,導致不平衡
        while(x != T->root && x->color == BLACK){
            if(x == x->p->left){
                rbt_t* w = x->p->right;

                //情況1 x的兄弟是紅色的,通過
                if(RED == w->color){
                    w->color = BLACK;
                    w->p->color = RED;
                    rbt_left_rotate(T,x->p);
                    w = x->p->right;
                }//處理完情況1之後,w.color== BLACK , 情況就變成2 3 4 了

                //情況2 x的兄弟是黑色的,並且其兒子都是黑色的。
                if(w->left->color == BLACK && w->right->color == BLACK){
                    if(x->p->color == RED){
                        x->p->color = BLACK;
                        w->color = RED;
                        x = T->root;//我也不知道為什麽
                        break;
                    }else{
                        w->color = RED;
                        x = x->p;//x.p左右是平衡的,但是x.p處少了一個黑結點,所以把x.p作為新的x繼續循環
                        continue;
                    }
                }

                //情況3 w為黑色的,左孩子為紅色。(走到這一步,說明w左右不同時為黑色。)
                if(w->right->color == BLACK){
                    w->left->color = BLACK;
                    w->color = RED;
                    rbt_right_rotate(T,w);
                    w = x->p->right;
                }//處理完之後,變成情況4

                //情況4 走到這一步說明w為黑色, w的左孩子為黑色, 右孩子為紅色。

                w->color=x->p->color;
                x->p->color=BLACK;
                w->right->color=BLACK;
                rbt_left_rotate(T,x->p);
                x = T->root;
            }else{
                rbt_t* w = x->p->left;
                //1
                if(w->color == RED){
                    w->color = BLACK;
                    x->p->color = RED;
                    rbt_right_rotate(T,x->p);
                    w = x->p->left;
                }
                //2
                if(w->left->color==BLACK && w->right->color == BLACK){
                    if(x->p->color == RED){
                        x->p->color = BLACK;
                        w->color = RED;
                        break;
                    }else{
                        x->p->color = BLACK;
                        w->color = RED;
                        x = x->p;
                        continue;
                    }
                }

                //3
                if(w->left->color == BLACK){
                    w->color = RED;
                    w->right->color = BLACK;
                    w = x->p->left;
                }

                //4
                w->color=w->p->color;
                x->p->color = BLACK;
                w->left->color = BLACK;
                rbt_right_rotate(T,x->p);
                x = T->root;
            }


        }
        x->color = BLACK;
    }


    //放心刪除todelete 吧
    free(toDelete);

    return T;
}


/*
*@brief rbt_left_rotate
*@param[in] T 樹根
*@param[in] x 要進行旋轉的結點
*/
void rbt_left_rotate( rbt_root_t* T, rbt_t* x){
    rbt_t* y = x->right;

    x->right = y->left;
    if(x->right != T->nil)
        x->right->p = x;



    y->p = x->p;
    if(y->p == T->nil){
        T->root = y;
    }else if(y->key < y->p->key)
        y->p->left = y;
    else
        y->p->right = y;

    y->left = x;
    x->p = y;
}
/*
*@brief rbt_right_rotate
*@param[in] 樹根
*@param[in] 要進行旋轉的結點
*/
void rbt_right_rotate( rbt_root_t* T, rbt_t* x){
    rbt_t * y = x->left;
    x->left = y->right;

    if(T->nil != x->left)
        x->left->p = x;



    y->p = x->p;
    if(y->p == T->nil)
        T->root = y;
    else if(y->key < y->p->key)
        y->p->left= y;
    else
        y->p->right = y;

    y->right = x;
    x->p = y;
}
void rbt_prePrint(const rbt_root_t* T, rbt_t* t){
    if(T->nil == t)return ;
    if(t->color == RED)
        printf("%3dR",t->key);
    else
        printf("%3dB",t->key);
    rbt_prePrint(T,t->left);
    rbt_prePrint(T,t->right);
}
void rbt_inPrint(const rbt_root_t* T, rbt_t* t){
    if(T->nil == t)return ;
    rbt_inPrint(T,t->left);
    if(t->color == RED)
        printf("%3dR",t->key);
    else
        printf("%3dB",t->key);
    rbt_inPrint(T,t->right);
}

//打印程序包括前序遍歷和中序遍歷兩個,因為它倆可以唯一確定一棵二叉樹
void rbt_print(const rbt_root_t* T){
    assert(T!=NULL);
    printf("\n前序遍歷 :");
    rbt_prePrint(T,T->root);
    printf("\n中序遍歷 :");
    rbt_inPrint(T,T->root);
    printf("\n");
}

void rbt_test(){
    rbt_root_t* T = rbt_init();

    /************************************************************************/
    /* 1    測試插入
    /*
    /*
    /*輸出  前序遍歷 :  7B  2R  1B  5B  4R 11R  8B 14B 15R
    /*      中序遍歷 :  1B  2R  4R  5B  7B  8B 11R 14B 15R
    /************************************************************************/

    T = rbt_insert(T,11);
    T = rbt_insert(T,7);
    T = rbt_insert(T,1);
    T = rbt_insert(T,2);
    T = rbt_insert(T,8);
    T = rbt_insert(T,14);
    T = rbt_insert(T,15);
    T = rbt_insert(T,5);
    T = rbt_insert(T,4); 

    T = rbt_insert(T,4); //重復插入測試
    rbt_print(T);

    /************************************************************************/
    /* 2    測試刪除
    /*    
    /*操作  連續刪除4個元素 rbt_delete(T,8);rbt_delete(T,14);rbt_delete(T,7);rbt_delete(T,11);
    /*輸出  前序遍歷 :  2B  1B  5R  4B 15B
    /*      中序遍歷 :  1B  2B  4B  5R 15B
    /************************************************************************/

    rbt_delete(T,8);
    rbt_delete(T,14);rbt_delete(T,7);rbt_delete(T,11);

    rbt_delete(T,8);//刪除不存在的元素
    rbt_print(T);

}

代碼只做了少量的測試,如果有BUIG請不吝指出。

版權聲明:原創不易,轉載請註明轉自weewqrer 紅黑樹


Tags:

文章來源:


ads
ads

相關文章
ads

相關文章

ad