機器喵之紅黑樹(二)

插入操做

因爲性質的約束:插入點不能爲黑節點,應插入紅節點。由於你插入黑節點將破壞性質5,因此每次插入的點都是紅結點,可是若他的父節點也爲紅,那豈不是破壞了性質4?對啊,因此要作一些「旋轉」和一些節點的變色!另爲敘述方便咱們給要插入的節點標爲N(紅色),父節點爲P,祖父節點爲G,叔節點爲U。下邊將一一列出全部插入時遇到的狀況:node

情形1:該樹爲空樹,直接插入根結點的位置,違反性質1,把節點顏色有紅改成黑便可。
情形2:插入節點N的父節點P爲黑色,不違反任何性質,無需作任何修改。函數

情形1很簡單,情形2中P爲黑色,一切安然無事,但P爲紅就不同了,下邊是P爲紅的各類狀況,也是真正要學的地方!spa

情形3:N爲紅,P爲紅,(祖節點必定存在,且爲黑,下邊同理)U也爲紅,這裏不論P是G的左孩子,仍是右孩子;不論N是P的左孩子,仍是右孩子。
操做:如圖把P、U改成黑色,G改成紅色,未結束。
圖片描述3d

解析:N、P都爲紅,違反性質4;若把P改成黑,符合性質4,顯然左邊少了一個黑節點,違反性質5;因此咱們把G,U都改成相反色,這樣一來經過G的路徑的黑節點數目沒變,即符合四、5,可是G變紅了,若G的父節點又是紅的不就有違反了4,是這樣,因此通過上邊操做後未結束,需把G做爲起始點,即把G看作一個插入的紅節點繼續向上檢索----屬於哪一種狀況,按那種狀況操做~要麼中間就結束,要麼知道根結點(此時根結點變紅,一根結點向上檢索,那木有了,那就把他變爲黑色吧)。調試


情形4:N爲紅,P爲紅,U爲黑,P爲G的左孩子,N爲P的左孩子(或者P爲G的右孩子,N爲P的左孩子;反正就是同向的)。
操做:如圖P、G變色,P、G變換即左左單旋(或者右右單旋),結束。
圖片描述code

解析:要知道通過P、G變換(旋轉),變換後P的位置就是當年G的位置,因此紅P變爲黑,而黑G變爲紅都是爲了避免違反性質5,而維持到達葉節點所包含的黑節點的數目不變!還能夠理解爲:也就是至關於(只是至關於,並非實事,只是爲了更好理解;)把紅N頭上的紅節點移到對面黑U的頭上;這樣即符合了性質4也不違反性質5,這樣就結束了。blog


情形5:N爲紅,P爲紅,U爲黑,P爲G的左孩子,N爲P的右孩子(或者P爲G的右孩子,N爲P的左孩子;反正兩方向相反)。
操做:須要進行兩次變換(旋轉),圖中只顯示了一次變換-----首先P、N變換,顏色不變;而後就變成了情形4的狀況,按照狀況4操做,即結束。
圖片描述接口

解析:因爲P、N都爲紅,經變換,不違反性質5;而後就變成4的情形,此時G與G如今的左孩子變色,並變換,結束。圖片


刪除操做

咱們知道刪除需先找到「替代點」來替代刪除點而被刪除,也就是刪除的是替代點,而替代點N的至少有一個子節點爲NULL,那麼,若N爲紅色,則兩個子節點必定都爲NULL(必須地),那麼直接把N刪了,不違反任何性質,ok,結束了;若N爲黑色,另外一個節點M不爲NULL,則另外一個節點M必定是紅色的,且M的子節點都爲NULL(按性質來的,不明白,本身分析一下)那麼把N刪掉,M佔到N的位置,並改成黑色,不違反任何性質,ok,結束了;若N爲黑色,另外一個節點也爲NULL,則把N刪掉,該位置置爲NULL,顯然這個黑節點被刪除了,破壞了性質5,那麼要以N節點爲起始點檢索看看屬於那種狀況,並做相應的操做,另還需說明N爲黑點(也許是NULL,也許不是,都同樣),P爲父節點,S爲兄弟節點(這個我真想給兄弟節點叫B(brother)多好啊,不過人家圖就是S我也不能改,在重畫圖,太浪費時間了!S也行呵呵,就當是sister也行,哈哈)分爲如下5中狀況:
情形1:S爲紅色(那麼父節點P必定是黑,子節點必定是黑),N是P的左孩子(或者N是P的右孩子)。
操做:P、S變色,並交換----至關於AVL中的右右中旋轉即以P爲中心S向左旋(或者是AVL中的左左中的旋轉),未結束。
圖片描述編譯器

解析:咱們知道P的左邊少了一個黑節點,這樣操做至關於在N頭上又加了一個紅節點----不違反任何性質,可是到經過N的路徑仍少了一個黑節點,須要再把對N進行一次檢索,並做相應的操做才能夠平衡(暫且無論往下看)。


情形2:P、S及S的孩子們都爲黑。
操做:S改成紅色,未結束。
圖片描述

解析:S變爲紅色後通過S節點的路徑的黑節點數目也減小了1,那個從P出發到其葉子節點到全部路徑所包含的黑節點數目(記爲num)相等了。可是這個num比以前少了1,由於左右子樹中的黑節點數目都減小了!通常地,P是他父節點G的一個孩子,那麼由G到其葉子節點的黑節點數目就不相等了,因此說沒有結束,需把P當作新的起始點開始向上檢索。


情形3:P爲紅(S必定爲黑),S的孩子們都爲黑。
操做:P該爲黑,S改成紅,結束。
圖片描述

解析:這種狀況最簡單了,既然N這邊少了一個黑節點,那麼S這邊就拿出了一個黑節點來共享一下,這樣一來,S這邊沒少一個黑節點,而N這邊便多了一個黑節點,這樣就恢復了平衡,多麼美好的事情哈!


情形4:P任意色,S爲黑,N是P的左孩子,S的右孩子SR爲紅,S的左孩子任意(或者是N是P的右孩子,S的左孩子爲紅,S的右孩子任意)。
操做:SR(SL)改成黑,P改成黑,S改成P的顏色,P、S變換--這裏相對應於AVL中的右右中的旋轉(或者是AVL中的左左旋轉),結束。
圖片描述

解析:P、S旋轉有變色,等於給N這邊加了一個黑節點,P位置(是位置而不是P)的顏色不變,S這邊少了一個黑節點;SR有紅變黑,S這邊又增長了一個黑節點;這樣一來又恢復了平衡,結束。


情形5:P任意色,S爲黑,N是P的左孩子,S的左孩子SL爲紅,S的右孩子SR爲黑(或者N是P的有孩子,S的右孩子爲紅,S的左孩子爲黑)。
操做:SL(或SR)改成黑,S改成紅,SL(SR)、S變換;此時就回到了情形4,SL(SR)變成了黑S,S變成了紅SR(SL),作情形4的操做便可,這兩次變換,其實就是對應AVL的右左的兩次旋轉(或者是AVL的左右的兩次旋轉)。
圖片描述

解析:這種狀況若是你按情形4的操做的話,因爲SR原本就是黑色,你沒法彌補因爲P、S的變換(旋轉)給S這邊形成的損失!因此我沒先對S、SL進行變換以後就變爲情形4的狀況了。

#include <stdio.h>
#include <stdlib.h>

#define RED 0
#define BACK 1

typedef int Elemtype;

//定義一個紅黑樹的結點
typedef struct Red_Back_Tree
{
    Elemtype e;
    int color;
    struct Red_Back_Tree * child[2];
}* RBT;

//    兩個節點變換函數
void conversion(RBT *T,int direction);

//    刪除一個節點的所用函數
int DeleteRBT(RBT *T,Elemtype e);                                //    刪除主(接口)函數
int find_replace_point(RBT gogal,RBT *l);                        //    尋找替代點
int keep_balance_for_delete(RBT *T,int direction);                //    刪除的平衡操做
int do_with_start_point(RBT gogal,RBT *T,int direction);                    //    處理第一個起始點

//    插入一個節點的所用函數
int InsertRBT(RBT *T,Elemtype e);                                //    插入接口函數
int _InsertRBT(RBT *T,Elemtype e);                                //    插入主函數
int keep_balance_for_insert(RBT *T,int firdirection,Elemtype e);//    插入的平衡操做
RBT create_one_node(Elemtype e);                                //    新建一個節點



void conversion(RBT *T,int direction)
{
    RBT f=(*T),s=f->child[direction],ss=s->child[!direction];

    f->child[direction]=ss;
    s->child[!direction]=f;
    *T=s;
}

//★★★★★★★★★★★★★★★★★刪除操做★★★★★★★★★★★★★★★★★★★★★★★★★★★

int do_with_start_point(RBT gogal,RBT *T,int direction)
{
    gogal->e=(*T)->e;
    if(BACK==((*T)->color))
    {
        if(NULL!=(*T)->child[direction])
        {
            (*T)->e=(*T)->child[direction]->e;
            free((*T)->child[direction]);
            (*T)->child[direction]=NULL;
            return 1;
        }
        else
        {
            free((*T));
            *T=NULL;
            return 0;
        }
    }
    else
    {
        free((*T));
        (*T)=NULL;
        return 1;
    }
}

int keep_balance_for_delete(RBT *T,int direction)
{
    RBT p=(*T),b=p->child[!direction];
    
    if(RED==b->color)
    {
        p->color=RED;
        b->color=BACK;
        conversion(&p,!direction);
        conversion(T,!direction);
        return keep_balance_for_delete(&((*T)->child[direction]),direction);
    }
    else if(BACK==p->color && BACK==b->color && 
        (NULL==b->child[0] || BACK==b->child[0]->color) && 
        (NULL==b->child[1] || BACK==b->child[1]->color))    
    {                                                        
        b->color=RED
        return    0; 
    }
    else if(RED==p->color && 
        (NULL==b->child[0] || BACK==b->child[0]->color) &&
        (NULL==b->child[1] || BACK==b->child[1]->color))
    {
        p->color=BACK;
        b->color=RED;
        return 1;
    }
//    第一次調試
//    調試緣由:因爲刪除0點未按預料的操做應該是狀況④,卻按⑤操做
//    錯誤的地方:RED==b->child[!direction] ! 丟了->color 這個錯誤我上邊錯了幾回,不過編譯器報錯改了過來
//    
//    else if(BACK==b->color && (NULL!=b->child[!direction] && RED==b->child[!direction]))
    else if(BACK==b->color && (NULL!=b->child[!direction] && RED==b->child[!direction]->color))
    {
        b->color=p->color;
        p->color=BACK;
        b->child[!direction]->color=BACK;
        conversion(T,!direction);
        return 1;
    }
    else
    {
        b->child[direction]->color=p->color;
        p->color=BACK;
        conversion(&(p->child[!direction]),direction);//這裏的p寫的纔算不錯!即p也(*T)都行,同樣!
        conversion(T,!direction);
        return 1;
    }

}

int find_replace_point(RBT gogal,RBT *l)
{
    if(NULL!=(*l)->child[0])
    {
        if(find_replace_point(gogal,&(*l)->child[0]))    return 1;
        return keep_balance_for_delete(l,0);
        //...
    }
//    
//    
//    
//          
//    else    //替代點爲起始點
//    {
//        return do_with_start_point(l,1);
//    }
    else
    {
        return do_with_start_point(gogal,l,1);
    }
}

int DeleteRBT(RBT *T,Elemtype e)
{
    if(!(*T))    return -1;
    else if(e>(*T)->e)
    {
        if(DeleteRBT(&((*T)->child[1]),e))    return 1;
        return keep_balance_for_delete(T,1);
        //...
    }
    else if(e<(*T)->e)
    {
        if(DeleteRBT(&((*T)->child[0]),e))    return 1;
        return keep_balance_for_delete(T,0);
        //...
    }
    else
    {
        if(NULL!=(*T)->child[1])    //真正的刪除點不是起始點,需找替代點
        {
            if(find_replace_point((*T),&((*T)->child[1])))    return 1;
            return keep_balance_for_delete(T,1);
            //...
        }
        else    //真正的刪除點就是起始點
        {
            return do_with_start_point((*T),T,0);
        }
    }
}
//★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★


//★★★★★★★★★★★★★★★★★★★插入操做★★★★★★★★★★★★★★★★★★★★★★★★★

RBT create_one_node(Elemtype e)
{
    RBT p=(RBT)malloc(sizeof(struct Red_Back_Tree));
    p->e=e;    p->color=RED;
    p->child[0]=p->child[1]=NULL;
    return p;
}

int keep_balance_for_insert(RBT *T,int firdirection,Elemtype e)
{
    RBT p=(*T)->child[firdirection],u=(*T)->child[!firdirection];
    int secdirection=( (e>p->e) ? 1 : 0 );    //    查處第二個方向
    
    if(NULL!=u && RED==u->color)    /*****③叔節點爲紅色*****/    
    {
        p->color=BACK;
        u->color=BACK;
        (*T)->color=RED;
        return 1;    //繼續...
    }
    else                            /*****④叔節點爲黑色*****/    
    {
        if(firdirection!=secdirection)    conversion(&((*T)->child[firdirection]),secdirection);
        (*T)->color=RED;    (*T)->child[firdirection]->color=BACK;
        conversion(T,firdirection);
        return 0;
    }
}

int _InsertRBT(RBT *T,Elemtype e)
{
    int info=0;
    if(NULL==(*T))                    /*****①插入到根節點*****/        //這裏只是包含這種狀況
    {
        *T=create_one_node(e);
        (*T)->color=RED;
        info=1;
    }
    else if(e>((*T)->e))
    {
        info=_InsertRBT(&(*T)->child[1],e);
        if(info<1)    return info;
        else if(info==1)            /*****②父節點爲黑******/
        {
            if(BACK==((*T)->color))    info--;
            else    info++;
        }
        else
        {
            info=keep_balance_for_insert(T,1,e);
        }
        
    }
    else if(e<((*T)->e))
    {
        info=_InsertRBT(&((*T)->child[0]),e);
        if(info<1)    return info;
        else if(info==1)    
        {
            if(BACK==((*T)->color))    info--;
            else    info++;
        }
        else
        {
            info=keep_balance_for_insert(T,0,e);
        }
        
    }
    else    return info=-1;
    
    return info;
}

int InsertRBT(RBT *T,Elemtype e)    //插入節點函數返回值: -1->改點已存在  0->成功插入
{
    int info=0;        //    info:  -1->已存在 0->結束 1->回溯到父節點 2->回溯到祖節點
    
//2011年11月30日9:13:47 昨天晚上最後又想來這裏這個if能夠不要便可,也就是把它也放到_InsertRBT
//內處理,在InsertRBT中有個判斷便可!即改爲下邊的寫法!
//    if(NULL==(*T))                    /*****①插入到根節點*****/
//    {
//        *T=create_one_node(e);
//        (*T)->color=BACK;
//    }
//    else            
//    {
//        info=_InsertRBT(T,e);    //    通過再三思考,這裏info的返回值只可能爲:-1  0  1
//        if(info>0)    (*T)->color=BACK,info=0;    //    查看根節點是否爲紅
//    }

    info=_InsertRBT(T,e);
    if(info==1)    (*T)->color=BACK,info=0;    
    //    爲了防止根結點變爲紅,它實際上是處理了兩種狀況的後遺症
//    分別是:③狀況回溯上來,根節點變紅  ①狀況插入點即爲根節點,爲紅
//    這裏沒有直接把根結點變黑,主要是爲了與_InsertRBT保持一致的寫法,其實都行!
    return info;
}
//★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★


//******************JUST FOR TEST********************//
RBT queue[1000];
void print(RBT cur)
{
    int front=0,rear=0;
    int count=1,temp=0;

    if(NULL==cur)    
    {
        printf("NULL\n");
        return ;
    }

    queue[rear]=cur;
    while(front<=rear)
    {
        cur=queue[front++];    count--;
        if(NULL!=cur->child[0])    queue[++rear]=cur->child[0],temp++;
        if(NULL!=cur->child[1])    queue[++rear]=cur->child[1],temp++;

        printf("%d color->",cur->e);
        if(BACK==cur->color)    printf("BACK |");
        else    printf("RED  |");
        
        if(0==count)
        {
            count=temp;
            temp=0;
            printf("\n");
        }
    }
}
//*****************************************************//

//*****************DEAR MAIN***************************//
int main()
{
    RBT T=NULL;
    int i,nodenum=100;
    
    print(T);
    printf("\n");

    printf("\n插入操做\n");
    for(i=0;i<nodenum;i++)
    {
        InsertRBT(&T,i);
        printf("插入%d\n",i);
        print(T);
        printf("\n");
    }

//    print(T);
    printf("\n刪除操做:\n");

    for(i=0;i<nodenum;i++)
    {
        DeleteRBT(&T,i);
        printf("刪除%d\n",i);
        print(T);
        printf("\n");
    }

    return 0;
}
相關文章
相關標籤/搜索