插入操作
由于性质的约束:插入点不能为黑节点,应插入红节点。因为你插入黑节点将破坏性质5,所以每次插入的点都是红结点,但是若他的父节点也为红,那岂不是破坏了性质4?对啊,所以要做一些“旋转”和一些节点的变色!另为叙述方便我们给要插入的节点标为N(红色),父节点为P,祖父节点为G,叔节点为U。下边将一一列出所有插入时遇到的情况:
情形1:该树为空树,直接插入根结点的位置,违反性质1,把节点颜色有红改为黑即可。
情形2:插入节点N的父节点P为黑色,不违反任何性质,无需做任何修改。
情形1很简单,情形2中P为黑色,一切安然无事,但P为红就不一样了,下边是P为红的各种情况,也是真正要学的地方!
情形3:N为红,P为红,(祖节点一定存在,且为黑,下边同理)U也为红,这里不论P是G的左孩子,还是右孩子;不论N是P的左孩子,还是右孩子。
操作:如图把P、U改为黑色,G改为红色,未结束。
解析:N、P都为红,违反性质4;若把P改为黑,符合性质4,显然左边少了一个黑节点,违反性质5;所以我们把G,U都改为相反色,这样一来通过G的路径的黑节点数目没变,即符合4、5,但是G变红了,若G的父节点又是红的不就有违反了4,是这样,所以经过上边操作后未结束,需把G作为起始点,即把G看做一个插入的红节点继续向上检索----属于哪种情况,按那种情况操作~要么中间就结束,要么知道根结点(此时根结点变红,一根结点向上检索,那木有了,那就把他变为黑色吧)。
情形4:N为红,P为红,U为黑,P为G的左孩子,N为P的左孩子(或者P为G的右孩子,N为P的左孩子;反正就是同向的)。
操作:如图P、G变色,P、G变换即左左单旋(或者右右单旋),结束。
解析:要知道经过P、G变换(旋转),变换后P的位置就是当年G的位置,所以红P变为黑,而黑G变为红都是为了不违反性质5,而维持到达叶节点所包含的黑节点的数目不变!还可以理解为:也就是相当于(只是相当于,并不是实事,只是为了更好理解;)把红N头上的红节点移到对面黑U的头上;这样即符合了性质4也不违反性质5,这样就结束了。
情形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;
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。