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

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

所有代码已经过编译,编译环境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


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值