红黑树学习笔记

来源:互联网 发布:ip切换软件 编辑:程序博客网 时间:2024/04/25 23:43

要学习红黑树,那么首先要知道红黑树到底是个什么东西,其实他的定义在网络到随处都可以搜索到,他是一种平衡二叉搜索树,在这个定义里面引入了三个关键词,一是二叉树,另一个是二叉搜索树,第三个是平衡。我们一层一层分析,

二叉树,他的定义是每个节点最多只有两个子树(即左子树和右子树,当然也可以没有子树),如下图是一个简单的二叉树:

对于这样的数据结构,在C语言中通常这样来定义结构体

?
1
2
3
4
5
typedefstruct _binary_tree{
struct _binary_tree *parent;//父节点指针
struct _binary_tree *left;//左子树指针
struct _binary_tree *right;//右子树指针
} BinaryTree;

二叉搜索树,首先他也是一个二叉树,与二叉树不同的是他加了一个属性值来进行排序,这个属性要满足两个条件,1、每个节点的左子树的值均小于该节点的值(在有左子树的情况下);2、每个节点的右子树的值均大于该节点的值(在有右子树的情况下),如下图是简单的二叉搜索树:

对于二叉搜索树的数据结构,在C语言中一般这样定义结构体:

?
1
2
3
4
5
6
typdef struct_binary_search_tree{
struct _binary_search_tree *parent;//父节点指针
struct _binary_search_tree *left;//左子树指针
struct _binary_search_tree *right;//右子树指针
int key;//用来排序的属性
} BinarySearchTree;

平衡,上面讲的两个都是数据结构,而这个不是数据结构,是一种衡量性能的指标,为什么这样说呢?因为你越是让这个树平衡,那么他的效率就越高,二分查找法就是利用这一点来达到高效的目地,那么如何让一颗红黑树平衡呢?这里先不讲如何让他平衡,我先告诉你平衡的定义,平衡即是左右子树的深度之差的绝对值不大于1,对于上面的二叉搜索树,他不满足平衡,因为左子树的深度是2,右子树的深度是4,他们之间的绝对值大于1,所以不平衡。

红黑树,终于到了本篇文章的主题,红黑树在涵盖了二叉树和二叉搜索树的内容之后,还加了一个颜色属性,这个颜色的目地就是为了让这个二叉搜索树更平衡(看清楚这里我用的是更平衡,并不是绝对平衡,因为红黑树是不能保证树100%平衡,只能说是尽可能的平衡,在这里千万别钻牛角尖),从而达到高效的目地。对于添加了这么一个颜色之后,红黑树又产生了新的条件,这产生的新条件就是红黑树的五大性质:1、节点颜色必须是红或者黑;2、根节点必须是黑色;3、每个叶子节点是黑色;4、每个红色节点的两个子节点都是黑色;5、从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点。在这里有很多初学者就很迷惑,这个性质到底什么意思?我要如何做才能保证红黑树的性质不被破坏?其实这里你只需要记住这五大性质只是用来判断一颗树是否是红黑树,至于要怎么做,这才是我们接下来要讲的重点。当在一颗红黑树上进行添加和删除操作后,有可能会破坏红黑的五大性质,那破坏了性质我们又该如何处理呢?对于每一种可能发生的情况,设计红黑树的作者都给出了相应的解决方案,对于开发人员而言,你只需要将每一种可能发生的情况对应的解决方案转换为代码即可。下面分别对添加和删除后可能发生的情况给出了解决方案。

添加节点后产生的5个情况如下
情况1:当父节点与叔父节点都是红色
解决方案:把祖父节点设为红色,父节点与叔父节点设为黑色,然后从祖父节点开始向上继续修复

情况2:当父节点是红色,父节点是祖父节点的左孩子,并且当前节点是父节点的右孩子
解决方案:使用父节点为轴进行左旋转,然后跳至情况3

情况3:父节点是红色,父节点是祖父节点的左孩子,并且当前节点是父节点的左孩子
解决方案:父节点设为黑,祖父节点设置红色,然后以祖父节点为轴进行右旋转

情况4:当父节点是红色,父节点是祖父节点的右孩子,并且当前节点是父节点的左孩子
解决方案:使用父节点为轴进行右旋转,然后跳至情况5

情况5:当父节点是红色,父节点是祖父节点的右孩子,并且当前节点是父节点的右孩子
解决方案:父节点设成黑色,祖父节点设为红色,以祖父节点为轴进行转旋转

删除节点后产生的情况如下:
在删除一个节点后,如果该节点的左、右子树都存在,那么用他右子树中最小的节点来替代这个被删除的节点,如果该节点只有左子树,或者只有右子树,那么就用他的左节点或者右节点来代替这个节点,所以在以下描述中的当前节点是表示一个被替换的新节点,并非被删除的那个节点,切记,切记。
情况1:当前节点是父节点的左孩子,兄弟节点为红色
解决方案:父节点染红,兄弟节点为黑,然后以父母节点为轴进行左旋转,然后重新获取兄弟节点

情况2:当前节点是父节点的左孩子,兄弟节点的左、右孩子为黑色
解决方案:兄弟节点染红,当前节点指向父节点,继续修复

情况3:当前节点是父节点的左孩子,兄弟节点的右孩子是黑色
解决方案:把兄弟节点的左孩子染为黑色,兄弟节点染为红色,以兄弟节点为轴进行右旋转,重新获取兄弟节点,然后跳转到情况4

情况4:当前节点是父节点的左孩子,兄弟节点的左、右孩子其中一个为红
解决方案:兄弟节点颜色为父节点颜色,父节点为黑色,兄弟节点右孩子为黑色,然后以父节点为轴进行左旋转

情况5:当前节点是父节点的右孩子,兄弟节点为红色
解决方案:兄弟节点染黑,父节点染红,以父节点为轴右旋转

情况6:同情况2

情况7:当前节点是父节点的右孩子,兄弟节点的左孩子为黑色
解决方案:兄弟节点的右孩子染黑,兄弟节点染红,以兄弟节点为轴进行左旋转,重新获取兄弟节点然后跳至情况8

情况8:当前节点是父节点的右孩子,兄弟节点的左、右孩子其中一个为红
解决方案:兄弟节点颜色染成父节点颜色,父节点为黑色,兄弟节点左孩子为黑色,以父节点为轴进行右旋转

上面的13种情况是红黑树在添加和删除节点时产生的,也配有相应的解决方案。细心的同学会发现,在上面的解决方案中多处用到了左旋转和右旋转,他们是如何实现的呢?其实这个在源代码中也有写,这里我们这样简单的来理解,左旋转即是将中心节点移到他右节点的左节点上,然后用他的右节点替换他的位置(如果中心节点的右节点的左节点已存在,那么将中心节点的右节点的左节点移到中心节点的右节点上);右旋转即是将中心节点移到他左节点的右节点上,然后用他的左节点替换他的位置(如果中心节点的左节点的右节点已存在,那么将中心节点的左节点的右节点移到中心节点的左节点上)。对于上面两个定义,有可能些同学会感觉有点绕口,但是如果根据这个定义你自己去画张图,你会理解的更好。


原创粉丝点击