【学习笔记】红黑树的实现(一):插入算法

来源:互联网 发布:网络司法拍卖司法解释 编辑:程序博客网 时间:2024/06/14 15:01

该文主要解释红黑树的插入和插入后的再平衡

所有代码已经过编译,编译环境ubuntu12.04LTS GCC 4.6.3

完整的红黑树代码已上传GitHub:https://github.com/xusongqi/Data_Structures

————————————————————————————————————————————

插入算法(插入+再平衡)在4月30日前后完成的,因为删除算法还没完成,就没有发这篇博文。

上午总算把删除算法弄得差不多了......

————————————————————————————————————————————

前一阵子写完了AVL树之后就有点手痒,想写写红黑树~五一前后就抽了几天,看了几篇关于红黑树的文章和代码,了解差不多之后就动手写了这片文章和这棵树。写之前先看了July的关于红黑树的系列文章,感觉不错不过还是有几个小细节没太理解,又陆陆续续找了几篇其他人的博客看了一下,原理get。其实红黑树经典(同样也是难点)的地方在于插入(和平衡)和删除(和平衡)。这样算起来,应该是有4个point:插入函数,插入再平衡函数,删除函数,删除再平衡函数。下面分别按顺序写一下学习过程中我对这4个point的感受。


红黑树的性质

首先,我们列出红黑树的性质,一共五条:

1)每个结点要么是红的,要么是黑的。
2)根结点是黑的。
3)每个叶结点,即空结点(NIL)是黑的。
4)如果一个结点是红的,那么它的俩个儿子都是黑的。
5)对每个结点,从该结点到其子孙结点的所有路径上包含相同数目的黑结点。


插入函数

插入在这四个当中其实还是比较简单的,因为基本原理和二叉树的差不多(二叉树的插入原理在早先文章有描述,此处不再赘述),我觉得一张流程图+代码足以~具体的细节在代码的主时尚写的比较详细。


流程图



代码

/*插入函数*/RB_Tree RB_Insert(int key, RB_Tree index){RB_Tree parent = NULL;//插入节点的父节点RB_Tree node = NULL;//插入节点/*判定:若辅助查找函数返回非空,则树中已存在相同的key值。返回root节点。 * (赋值表达式返回的值等于表达式左值)*/if( node = RB_Search_Assist(key, index, &parent) ){return index;}/*开辟节点(已从辅助查询函数中得到插入点的parent位置)*/node = (RB_Tree)malloc(sizeof(RB_Node));node->color = RED;//新节点初始化为红色node->key = key;//写入key值node->parent = parent;//之前带回了parent位置node->leftChild = NULL;node->rightChild = NULL;/*插入新节点*/if(parent)//若parent不为空(树非空){if(key < parent->key){parent->leftChild = node;}else{parent->rightChild = node;}}else//!parent树为空,当前节点成为root点{index = node;}/*树的再平衡*/return RB_Insert_Rebalance(node, index);}

插入再平衡函数

先上一张流程图感受一下~

那么:

在插入节点后,因为我们默认了新节点是红色,所以可能会破坏第2条和第5条。所以我们需要对树进行再平衡,让它符合红黑树的性质。

若插入的是根结点,直接染黑,OK。

若插入后的父节点是黑的,不需要平衡,直接OK。

除了上面的两种简单情况,我们还会遇到下面三种比较一般的情况:

【注意:以下三种情况均为父节点为祖父节点的左孩子时候的情况,当父节点为祖父节点的右孩子时请镜像反转(left<->right)】


1)当前节点的父节点为红色,且叔叔节点(祖父节点的另一个节点)为红色。

解决办法:将祖父节点染红(因为父节点为红色,所以祖父节点一定为黑色,否则插入前就已经不是红黑树),父节点和叔叔节点染黑。将祖父节点成为新的当前节点,继续算法。(未改变性质五)

以下两张图片引自博客【结构之法,算法之道】

调整前:


调整后:



2)父节点为红,叔叔节点为黑色,当前节点为父节点的右孩子。

解决办法:将父节点作为新的当前节点,并将其左旋,继续算法(直接到情况3)。(未改变性质五)

以下两张图片引自博客【结构之法,算法之道】

调整前:


调整后:



3)父节点为红色,叔叔节点为黑色,且当前节点为父节点的左孩子。

解决办法:将父节点染黑,祖父节点染红,并右旋祖父节点。(改变了性质五,树平衡,结束算法。)

以下两张图片引自博客【结构之法,算法之道】

调整前:



调整后:




最后附上代码:

/*插入后再平衡函数*/static RB_Tree RB_Insert_Rebalance(RB_Tree node, RB_Tree index){RB_Tree parent, grandpa, uncle, temp;//父节点,祖父节点,叔叔节点,临时节点/*循环体:校正从插入点开始到root结束*/while((parent = node->parent) && (parent->color == RED)){grandpa = parent->parent;/*当父节点为祖父节点的左孩子*/if(parent == grandpa->leftChild){uncle = grandpa->rightChild;/*情况一:父节点与叔节点均为红色*/if(uncle && uncle->color == RED){uncle->color = BLACK;//叔节点染黑parent->color = BLACK;//父节点染黑grandpa->color = RED;//祖父节点染红node = grandpa;//指向祖父节点,继续算法}else//叔节点为黑(为黑或为空){/*情况二:叔节点为黑,当前节点为父节点右孩子*/if(node == parent->rightChild){index = RB_Rotate_Left(parent, index);//以父节点为轴,左旋temp = parent;parent = node;node = temp;//并将指针指向旋转前的父节点,继续算法}//注意:情况二必然导致情况三;但情况三未必经过情况二而来/*情况三:叔节点为黑,当前节点为父节点的左孩子*/parent->color = BLACK;//父节点染黑grandpa->color = RED;//祖父节点染红index = RB_Rotate_Right(grandpa, index);//以祖父节点为轴,左旋;继续算法//情况三的调整结束之后,node的父节点变成黑色,调整结束。}}/*父节点为祖父节点右孩子,情形与上类似但镜像对称*/else//parent == grandpa->rightChild{uncle = grandpa->leftChild;/*情况一:父节点与叔节点均为红色*/if(uncle && uncle->color == RED){uncle->color = BLACK;//叔节点染黑parent->color = BLACK;//父节点染黑grandpa->color = RED;//祖父节点染红node = grandpa;//指向祖父节点,继续算法}else//叔节点为黑(为黑或为空){/*情况二:叔节点为黑,当前节点为父节点左孩子*/if(node == parent->leftChild){index = RB_Rotate_Right(parent, index);//以父节点为轴,右旋temp = parent;parent = node;node = temp;//当前节点指向旋转前的父节点,继续算法}//注意:情况二必然导致情况三;但情况三未必经过情况二而来/*情况三:叔节点为黑,当前节点为父节点右孩子*/parent->color =BLACK;//父节点染黑grandpa->color = RED;//祖父节点染红index = RB_Rotate_Left(grandpa, index);//以祖父节点为轴,左旋;继续算法}}}index->color = BLACK;//root节点必然为黑色,没有为什么。return index;//返回root节点}

参考资料:

你透彻了解红黑树—结构之法,算法之道

红黑树的C实现完整源码—结构之法,算法之道

红黑树从头至尾插入和删除结点的全程演示图—结构之法,算法之道

条理清晰的学习红黑树(插入节点)—骥在路上

(下面这个我就纳闷怎么就属于违禁url了)

数据结构之红黑树C源码实现与剖析—梦醒潇湘love

       blog.chinaunix.net/uid-26548237-id-3479927.html


0 0
原创粉丝点击