红黑树系列二:红黑树的插入

来源:互联网 发布:经济学推荐书籍 知乎 编辑:程序博客网 时间:2024/06/11 03:57


红黑树的节点结构:

//定义红黑树的数据结构  typedef enum colorType{Red,Black} colorType;  typedef int elementType;  typedef struct RedBlackNode  {      struct RedBlackNode *parent;  //父节点      struct RedBlackNode *Left;   //指向左节点      struct RedBlackNode *Right;  //指向右节点      colorType color;            //j节点颜色,非黑即红      elementType element;       //节点存放的数据        }RBTree,*PRBTree;  



更多红黑树特性请参考:http://blog.csdn.net/lpp0900320123/article/details/39524947

一、树的旋转

因为红黑树的插入会破坏树的红黑平衡性,需要调整的树的结构,也就是通过旋转调整树的结构


1.左旋


以pivot为旋转点,左旋后树变为右边的结构。

伪算法:(根节点中有三个指针,左、右、父;每当节点关系发生变化时,记得更新这三个指针)

(1)把Y的左子树变成pivot的右子树

pivot->Right=Y->left 

Y->left->parent=pivot;  (b节点的父亲变为pivot)

(2)Y代替pivot的位置,分三种情况考虑pivot为根、P的左子树、P的右子树

Y->parent=pivot->parent  //把pivot的父亲继承过来

为根:root=Y

P的左子树:pivot->parent->left=Y

P的右子树:pivot->parent->Right=Y

(3) pivot为Y的左子树

Y->left=pivot

pivot->parent=Y

C代码的实现:

PRBTree LeftRotate(PRBTree x,PRBTree root){PRBTree y=x->Right;  //保存x的右子树//(1)x->Right=y->Left;if(y->Left!=NULL)y->Left->Parent=x;//(2)y要完全顶替x的位置(必须将其对父节点的关系完全接受过来)y->Parent=x->Parent;if(x==root)root=y;  else if(x==x->Parent->Left)  //x为其父节点的左子节点x->Parent->Left=y;    else if(x==x->Parent->Right)x->Parent->Right=y;     //x为其父节点的左子节点//(3)y->Left=x;x->Parent=y;return root;}


2.右旋

伪算法:

(1)把Y的右子树做为pivot的左子树

pivot->left = Y->right  ,  Y->right->parent = pivot;

(2)Y代替pivot的位置,分三种情况考虑pivot为根、P的左子树、P的右子树

Y->parent=pivot->parent  //把pivot的父亲继承过来

为根:root=Y

P的左子树:pivot->parent->left=Y

P的右子树:pivot->parent->Right=Y

(3)pivot为Y的右子树

Y->Right = pivot , pivot->parent = Y;

C代码:

//右旋PRBTree RightRotate(PRBTree x,PRBTree root){PRBTree y=x->Left;  //保存x的左子树//(1)x->Left=y->Right;if(y->Right!=NULL)y->Right->Parent=x;//(2)y要完全顶替x的位置(必须将其对父节点的关系完全接受过来)y->Parent=x->Parent;if(x==root)root=y;  else if(x==x->Parent->Left)  //x为其父节点的左子节点x->Parent->Left=y;    else if(x==x->Parent->Right)x->Parent->Right=y;     //x为其父节点的右子节点//(3)y->Right=x;x->Parent=y;return root;}

二、红黑树插入

红黑树节点插入和二叉搜索树插入相似,只不过插入节点后可能会破坏红黑树平衡性,需要做平衡处理。为了继续保持红黑树的性质,可以通过对结点进行重新着色,以及对树进行相关的旋转操作,即通过修改树中某些结点的颜色及指针结构,来达到对红黑树进行插入或删除结点等操作后继续保持它的性质或平衡的目的

   红黑树的平衡条件:

     1、每个节点不是红色就是黑色。
     2、根节点为黑色。
     3、如果节点为红色,其子节点必须为黑色。
     4、任意一个节点到到NULL(树尾端)的任何路径,所含之黑色节点数必须相同。



      我们首先以二叉查找树的方法增加节点并标记它为红色。  如果设为黑色,就会导致根到叶子的路径上有一条路上,多一个额外的黑节点,这个是很难调整的。但是设为红色节点后,可能会导致出现两个连续红色节点的冲突,那么可以通过颜色调换(color flips)和树旋转来调整。)

     设新增节点为X,其父节点为P, 节点为U,曾祖父节点为GG。

(1)如果插入节点X为树的根节点(违反性质1),把节点颜色改为黑;

(2)如果插入节点X的父节点为黑色,则插入完成,不需要做平衡处理。

(3)如果插入节点X的父节点P为红色(红-红,违反了平衡条件3,必须调整树形),则G比为黑(因为原RBT树必须遵循规则3)。根据插入节点X的位置和(U和GG)的颜色分以下几种情况考虑:(已假定P的红色)

状况1:U为黑色,X外侧插入;对节点G、P做一次旋转,并更改P、G的颜色,即可重新满足红黑树的规则3


状况2:U为黑色,X内侧插入;对节点P、N做一次旋转------->状况1(对G、N做一次旋转,并更改G、N的颜色)


状况3:U 为红色;此时P、U更改为黑色,G更改为红色,未结束(如果G的父亲为红色,有出现红红,把G做为新插入的节点,继续向上检索,看属于状况1、2、3的哪种情况并做相应的处理)


c代码:

//插入平衡处理PRBTree Insert_Rebalance(PRBTree x,PRBTree root){x->color=RED;  //插入节点为红色while(x!=root&&x->Parent->color==RED) //插入节点的父节点为红色并且不为根节点{if(x->Parent==x->Parent->Parent->Left) //父节点为祖父节点的左子节点{PRBTree y=x->Parent->Parent->Right; //y为插入节点的伯父节点if(y&&y->color==RED)  //伯父节点存在且为红{//状况3,更改颜色x->Parent->color=BLACK;y->color =BLACK;x->Parent->Parent->color=RED;x=x->Parent->Parent;  //把x的祖父节点作为新插入的节点}else  //伯父节点不存在或者伯父节点为黑(状况1、2){if(x==x->Parent->Right) //在右边插入,内侧属于状况2{x=x->Parent;root=LeftRotate(x,root);  //x为左旋点}x->Parent->color=BLACK;      //本来为红色,变为黑色x->Parent->Parent->color=RED; //本来为黑色,变为红色root=RightRotate(x->Parent->Parent,root);}}else //父节点为祖父节点的右子节点{PRBTree y=x->Parent->Parent->Left; //y为插入节点的伯父节点if(y&&y->color==RED)  //伯父节点存在且为红{//状况3,更改颜色x->Parent->color=BLACK;y->color =BLACK;x->Parent->Parent->color=RED;x=x->Parent->Parent;  //把x的祖父节点作为新插入的节点}else  //伯父节点不存在或者伯父节点为黑(状况1、2){if(x==x->Parent->Left) //在左边插入,内侧属于状况2{x=x->Parent;root=RightRotate(x,root);  //x为右旋点}x->Parent->color=BLACK;      //本来为红色,变为黑色x->Parent->Parent->color=RED; //本来为黑色,变为红色root=LeftRotate(x->Parent->Parent,root);}}} //while循环结束root->color=BLACK;  //根的颜色置为黑色return root;}

插入函数的C代码:

PRBTree RBT_Insert(elementType data,PRBTree root){PRBTree node=root;PRBTree parent=NULL;//查找插入点的位置while(node){parent=node;if(data<node->element)node=node->Left;else if(node->element<data)node=node->Right;elsereturn root;  //直接返回根节点,插入节点不可以有重复元素;}//离开while循环,parent指向插入节点父节点(此时比为叶子节点)//为新插入的节点分配内存并初始化node=(struct RedBlackNode *)malloc(sizeof(struct RedBlackNode));node->element=data;node->Parent=parent;  //新插入节点的父节点node->Left=node->Right=NULL;if(parent)  {    //判断要插入左子树还是右字树if(parent->element<data){parent->Right=node;}else if(data<parent->element){parent->Left=node;}}else  //插入的第一个节点作为根节点{root=node;}//平衡处理return Insert_Rebalance(node,root);}

三、代码的测试

1.树的遍历函数(中序)

void RBT_Printf(PRBTree root){//中序遍历if(NULL==root)return;RBT_Printf(root->Left);    //0 红色  1黑色printf("%d,%d ",root->element,root->color);RBT_Printf(root->Right);}

2.主函数

int main(){PRBTree T=NULL;//T=Init_RBTree(T);T=RBT_Insert(10,T);T=RBT_Insert(85,T);T=RBT_Insert(15,T);T=RBT_Insert(70,T);T=RBT_Insert(20,T);T=RBT_Insert(60,T);T=RBT_Insert(30,T);T=RBT_Insert(50,T);T=RBT_Insert(65,T);T=RBT_Insert(80,T);T=RBT_Insert(90,T);T=RBT_Insert(40,T);T=RBT_Insert(5,T);T=RBT_Insert(55,T);RBT_Printf(T);}

输出结果:



红黑树的树状图:

其中双圈圈的为红色,单圈圈的为黑色。


0 0
原创粉丝点击