因爲性質的約束:插入點不能爲黑節點,應插入紅節點。由於你插入黑節點將破壞性質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; }