初步了解红黑树

二叉查找数

红黑树本质上就是一颗二叉查找数,所以在了解红黑树之前,需要先了解二叉查找树

二叉查找树(Binary Search Tree),也称二叉搜索树,有序二叉树(ordered binary tree),排序二叉树(sorted binary tree),是指一颗空树或者具有下列性质的二叉树:

  1. 若任意节点的左子树不空,则左子树上的所有节点的值均小于它的根节点的值;
  2. 若任意节点的右子树不空,则右子树上的所有节点的值均大于它的根节点的值;
  3. 任意节点的左、右子树也分别为二叉查找树;
  4. 没有键值相等的节点;
    二叉查找树相比于其他数据结构的优势在于查找、插入的时间复杂度较低,为O(logn)。二叉查找树是基础性数据结构,用于构建更为抽象的数据结构,如果集合、multiset、关联数组等。

关于二叉查找树的查找,单位时间运算,使的问题的复杂度减少一半,变为原来的1/2,经过x次查找找到目标。所以2的x次方=n ,所以x=logn。logn是以2为底的n的对数。

红黑树

红黑树是一种自平衡二叉查找树。
红黑树是一种比较常用的数据结构,它使得对数据的搜索,插入和删除操作都能保持在O(logn)的时间复杂度。相比于其他的数据结构,红黑树的实现难度有所增加
红黑树相对于二叉查找树增加了着色和想关性质以及自平衡功能

性质

  1. 每个节点要么是红的,要么是黑的。
  2. 根节点是黑的。
  3. 每个叶节点(叶节点即指树尾端NIL或NULL节点)是黑的
  4. 如果一个节点是红的,那么它的两个儿子都是黑的。
  5. 对于任一节点而言,其到叶节点树尾端NULL节点的每一条路径都包含相同数目的黑节点。这个路径不能是向父节点查找。

红黑树的图片

《初步了解红黑树》 红黑树

数据结构

基本属性:节点的颜色、左子节点、右子节点、父节点、节点的值

《初步了解红黑树》 基本数据结构

红黑树的方法核心主要在于插入和删除,因为插入和删除是最复杂的。

红黑树的插入操作

默认插入节点的颜色都是红色,因为插入黑色节点会破坏路径上的黑色节点总数。
如果红黑树为空,将根节点指向新插入的节点,将该节点的颜色设为黑色。
如果红黑树不为空,就需要递归查找到需要的节点,然后在该节点的下进行插入操作。
红黑树在插入上虽说都是红色,并不会破坏路径上的黑色节点数,但是会遇到连续插入两个节点都是红色的情况,这种情况就破坏了红黑树的性质,此时就需要对红黑树进行调整。红黑树的调整操作一般都是通过旋转结合节点的变色操作来完成的。

在节点插入时,红黑树是平衡的,插入后破坏了平衡,此时祖父节点一定是存在的,而且祖父节点的颜色一定是黑色。因为祖父节点不存在,就表示在根节点的下方插入数据,根节点是黑色的,又不会破坏平衡。同时如果祖父节点是红色,那么原来的树就不是红黑树。
此时重点需要考虑的问题是叔父节点的颜色,因为叔父节点颜色不同进行的平衡操作也是不一样。
此时冲突情况如下图所示:
n:表时新插入的节点
p:表示插入节点的父节点
gp:表示插入节点的祖父节点
u:表示插入节点的的叔叔节点,颜色不定。

《初步了解红黑树》 红黑树冲突情况

红黑树的插入后平衡调整1:叔父节点是黑色

关于叔父节点是黑色,一共有四种插入操作
A:n是p的左子节点,p是g的左子节点
B:n是p的右子节点,p是g的右子节点
C:n是p的左子节点,p是g的右子节点
D:n是p的右子节点,p是g的左子节点
情况A、B统一称为外侧插入,C、D统一称为内侧插入,他们的解决方式是对称的,就是A和B的解决方式是对称,解决方式统一称为单旋转。C、D统一解决方式称为双旋转。

情况A:左外侧插入调整

关于情况A,是由其他情况变化过来的,或者叔叔节点是空NULL节点。
以上推论基于红黑树性质5,如果原来的P的所有子节点都是NULL节点,那么g到NULL节点路径的黑色点是两个,那么u节点一定是叶子NULL节点。如果不是叶子节点那么就不满足性质5。如果不满足性质5那么此状态一定是一个中间态。这个中间态必然是在n节点转换之后的中间态。

该情况需要以P节点为旋转点,将树右旋转。此时如果p点有右子节点,那么就该节点设为g节点的左节点,同时将p节点父节点指向g节点的父节点。旋转完成后将节点p着色为黑色,将节点g着色为红色。
经过以上变化:那么g到叶子节点满足红黑树性质5,从根节点到g经过的黑色节点数量未变化,所以也满足性质5,总体变化后树满足红黑树的性质。
该情况调整如下图所示:

《初步了解红黑树》 情况A:左外侧插入调整

情况B:右外侧插入调整

与情况A相反操作即可,即左旋
该情况调整如下图所示:

《初步了解红黑树》 情况B:右外侧插入调整

情况C:右内侧插入

该情况需要进行一次相反的双旋转操作。
先以n节点为旋转点,进行右旋,变成情况B,然后进行一次左旋,旋转之后就平衡了。
该情况的调整如下图所示:

《初步了解红黑树》 情况C:右内侧插入调整

情况D:左内侧插入

该情况与情况D对称相反。
该情况的调整入下图所示:

《初步了解红黑树》 情况D:左内侧插入调整

红黑树的插入后平衡调整2:叔父节点是红色

在插入前树是红黑树,那么从根节点到叶子(NULL)节点路径经历的黑色节点树必然一致。
该种情况不能直接调整,需要进行的操作有两步,
1、将父节点和叔叔节点均涂为黑色,将祖父节点涂为红色,此时依然满足性质5,就算新插入节点,也不会破坏性质5。此时有可能破坏的性质就是性质4。
2、如果破坏了性质4,那么必然是祖父节点的父节点也是红色,此时将祖父节点当做新插入的节点,然后判断是否需要进行相应平衡调整。平衡调整的两种情况循环遍历即可,直到父节点或者祖父节点是根节点即可或者满足相应性质,最后将根节点涂黑。
具体操作如下方两图所示:

《初步了解红黑树》 图1:叔父节点为红的插入
《初步了解红黑树》 图2:消除连续红色节点

红黑树插入操作总结

  1. 红黑树总体分为两大类,4小类操作,插入之后会产生一种情况,这种情况在解决一次之后会变化成其他情况。
  2. 红黑树使用自底向上插入,递归遍历整条树,然后调整,使得整条树满足红黑树条件。

红黑树的删除操作

如果待删除的节点拥有唯一节点,或者没有子节点,则将该节点删除,并将其子节点(空节点)代替自身位置。如果待删除节点有两个子节点,则不能将该节点直接删除。而是从其右子树中选取最小节点的值作为删除节点(该节点一定没有两个子节点了,否则还能取更小的值),当然删除被选取的节点之前,需要将被选取的节点数据copy到原来需要删除的节点中。
关于删除点的选定参考下图:

《初步了解红黑树》 删除点的选定

上图中:用红色标记的节点表示被选定的真正删除的节点(节点y),其中绿色节点(y-old)表示原本需要删除的位置,而由于它有两个子节点,因此删除y代替他,并且删除y之前需要将y的值copy到y-old中,这里如果是红黑树也不会改变y-old的颜色。通过上述方式,将所有的节点删除简化为独立后继的节点删除问题。然后考虑删除y后的红黑树平衡调整问题。

在删除节点后,原红黑树的性质可能被改变。
如果删除的是红色节点,那么原红黑树的性质依旧保持不变,此时不需要做修正操作,删除节点的后继(null)节点是黑色,那么从根节点到这个后继(null)节点依旧满足性质5。
如果删除的是黑色节点,原红褐色的性质可能会被改变,我们要对其做修正操作。
那么哪些树的性质会发生变化呢,如果删除节点不是树的唯一节点,那么删除节点的那一个支的节点到个叶节点的黑色节点数会发生变化,此时性质5被破坏。
如果被删除节点的唯一非空子节点是红色,而被删除节点的父节点也是红色,那么性质4被破坏。
如果被删除节点是根节点,而它的唯一非空子节点是红色,则删除后新根节点将变成红色,违反性质2

关于删除节点后的修复问题。
我们从被删除节点后来顶替它的那个节点开始调整,这个节点记录为x,并认为它有额外的一层黑色。这里额外一重黑色是什么意思呢,我们不是把红黑树的节点加上除红与黑的另一种颜色,这里只是一种假设。我们认为我们当前指向它,因此空有额外一种黑色,可以认定它的黑色是从父节点被删除之后继承给它的,它现在可以容纳两种颜色,如果它原来是红色,那么现在是红+黑,如果原来是黑色,那么它现在的颜色是黑+黑。有了这层额外的黑色,原红黑树的性质5就能保持不变。现在只需要恢复其它性质就可以了,做法还是尽量向根移动和穷举所有可能性。

将删除情况分为以下几种情况
1、当前节点颜色是红+黑色,就是待删除的节点是黑色,顶替它的节点是红色,
解法:只需要将红色节点涂成黑色即可。
2、当前节点是黑+黑,且是根节点,
解法:什么都不做,或者说将根节点再涂一遍黑色就行了。。。
3、当前节点是黑+黑且兄弟节点是红色(此时父节点和兄弟节点的子节点都为黑)
解法:将兄弟节点涂成黑色,将父节点涂成红色,以兄弟节点为旋转点,左旋转。此时兄弟节点指向新的兄弟节点。
变换方式如下图所示其中
X:表示替换原来节点的节点
P:表示X节点的父节点
SIB:表示兄弟节点,SIB是sibling的缩写
其他两个分别表示SIB节点的子节点。

《初步了解红黑树》 情况3

具体分析:
变换前
假定从X到叶子节点的路径中黑色节点数是A(不包含X),那从P到经过X到叶子节点数就是A+3,未失去平衡。那么从P经SIB到达叶子节点数必然也是A+3。那么SIB经过SL或SR到达叶子节点数是A+2

变换后
这条子树根节点变为原来的SIB节点
从SIB经过X到达叶子节点路径依旧包含A+3黑色节点
从SIB经过SL或SR到达叶子节点路径信息因SIB变成了黑色依旧是A+3个黑色节点。

此时还有最重要的需要处理就是后面遇到情况4、5、6。
因为变换后X含有两重黑色,需要去掉一重,这一重颜色只能加到图中所示的P节点上,其他节点的颜色修改都有可能导致其他性质的变化。在P节点变为黑色之后,就需要考虑SL的子节点的颜色了。
而最后将P修改为黑色操作是在完全变化之后进行的。

这个就是后续情况了,也就是为什么SIB就指向了SL

4、当前节点是黑+黑,且兄弟节点是黑色,且兄弟节点的两个子节点全为黑色
解法:将兄弟节点涂成红色,将X节点指向原X节点的父节点。

《初步了解红黑树》 情况4

具体分析:
变换前:
假定从X到叶子节点的路径中黑色节点数是A(不包含X),那从P到经过X到叶子节点路径中黑色节点数就是A+2+?,未失去平衡。那么从P经SIB到达叶子节点路径中黑色节点数必然也是A+2+?。那么SIB经过SL或SR到达叶子节点路径中黑色节点数是A+2。”?”不是0就是1

变换后:
这条子树的根节点没有变化,那么从表现上看需要将P和SIB的颜色重新调换就OK了。但是并不是这样。
如果P节点是红色,那么只需要将SIB涂成红色,最后将P节点涂成黑色就OK了,这样这个树已经恢复成红黑树了。
但是情况并没有那么简单,如果P是黑色的,那么就需要向上移动及穷举。因为这个子树自己内部调整已经没有办法恢复成红黑树了。所以需要重新从情况1开始判断….,那么重新判断时,X指向了P节点。

5、当前节点的颜色是黑+黑,兄弟节点是黑色,兄弟节点的左子是红色,右子是黑色。
解法:将兄弟节点左子涂黑,将兄弟节点涂成红色,然后以兄弟节点的左子为中心点右旋。
具体情况如下图所示

《初步了解红黑树》 情况5

具体分析:
变换前:
假定从X到叶子节点的路径中黑色节点数是A(不包含X),那从P到经过X到叶子节点的路径中黑色节点就是A+2+?,未失去平衡。那么从P经SIB到达叶子节点路径中黑色节点数必然也是A+2+?。那么SIB经过SL到达叶子节点路径中黑色节点数必然是A+2,SIB经过SR到达叶子节点数是A+2。”?”不是0就是1。
变化后:
原以SIB为根的子树,从新顶点到叶子节点的路径中包含的黑色节点树没有变化,依然是A+2。如果将SIB指向新子树的根节点,那么此时情况就变成了情况6,就可以按照情况6进行相应的操作

6、当前节点的颜色是黑+黑,兄弟节点是黑色,兄弟节点的左子是红色,右子是黑色。
解法:把兄弟节点颜色染成当前父节点的颜色,把当前父节点的颜色染成黑色,兄弟节点右子染成黑色,然后以兄弟节点为旋转的进行左旋。
具体情况如下图所示

《初步了解红黑树》 情况6

具体分析:
变换前:
X含有的颜色数是2,黑+黑
假定从X到叶子节点的路径中包含A个黑色节点,不含X节点颜色,那么从P节点开始到叶子节点路径中必然含有A+2+?个黑色节点,含P。
从P开始经SL的路径也必然含有A+2+?个黑色节点,含P。
从P开始经SRL的路径也必然含有A+2+?个黑色节点,含P。
从P开始经SRR的路径也必然含有A+2+?个黑色节点,含P。

变换后:
X含有的颜色数变为1
当前子树节点变成了原来的兄弟节点。
在变换过程中并不涉及X的叶子节点颜色变化,所以上面的假设依然成立,所以从X到叶子节点的路径中包含A个黑色节点,不含X。
从新的根节点开始经X到叶子节点的路径,含有A+2+?个黑色节点
从新的根节点开始经SL到叶子节点的路径,含有A+2+?个黑色节点
从新的根节点开始经SRL到叶子节点的路径,含有A+2+?个黑色节点
从新的根节点开始经SRR到叶子节点的路径,含有A+2+?个黑色节点

红黑树删除流程

具体如下图

《初步了解红黑树》 删除流程图

其他

参考1:二叉查找树
参考2:维基百科—二元搜寻树
参考3:红黑树数据结构剖析
参考4:红黑树插入删除演示

    原文作者:史云龙
    原文地址: https://www.jianshu.com/p/b266c14c5e9e
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞