因为性质的约束:插入点不能为黑节点,应插入红节点。由于你插入黑节点将破坏性质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; }