红黑树介绍与算法分析

来源:互联网 发布:js选下一个兄弟节点 编辑:程序博客网 时间:2024/06/06 05:08

描述

红黑树是一种特殊的二叉查找树,二叉查找树的性质在这不详细介绍。红黑树的节点除了需要存储key值外,还需要保存一个color信息。下图是一个简单的红黑树


属性

1.所有节点都是有颜色的,red orblack

2.root节点是black

3.叶节点都是black

4.red节点的左右子节点的颜色必须是black

5.从任何一个内部节点(包括根节点)开始,到任何一个叶节点,所有路径上的黑色节点数量相等,计算黑色节点时,不包括起始节点。


我们通常画红黑树的时候常常忽略叶节点,如下


常用操作

Insert:插入一个节点

红黑树的插入操作完成后需要保证新的红黑树不违反红黑树的5条性质。

算法设计(来自《算法导论》):

每次插入一个新的节点时,我们都将新的节点设置为红色,然后按照二叉查找树的插入算法进行操作。

插入完成后,我们分析一下新的树可能会违反哪些属性。重新复习一下红黑树的性质:

 

1.所有节点都是有颜色的,red or black

2.root节点是black

3.叶节点都是black

4.red节点的左右子节点的颜色必须是black

5.从任何一个内部节点(包括根节点)开始,到任何一个叶节点,所有路径上的黑色节点数量相等,计算黑色节点时,不包括起始节点。

 

 

因为插入的是一个red节点,所以性质1,3,5不会受到影响,那么2和4在哪些情况下被违反呢?

Case1:如果我们插入的节点刚好是作为根节点,那么违反了性质2

    对于case1,只需改变节点颜色即可,红色黑色即可。

Case2:如果插入节点的父节点是red,那么就违反了性质4。

但是对于case2,需要分析了。先记这个新插入的节点为z,他的父节点为p(z),祖父节点是p(p(z))。
因为color[z]=red, color[p(z)]=red,根据性质4,所以color[p(p(z))]=black,因为要保持属性5,即从p(p(z))到叶节点的黑色节点数量一致,所以p(p(z))如果有另外的一个子树,那么另外一个子节点肯定是red。
因为z与p(z) ,p(z) 与p(p(z))的左右子树的关系,可以分4种情况,我们可以穷举:z是p(z)左树;z是p(z)右树; p(z)是 p(p(z))左树;p(z)是 p(p(z))右树。两两组合,共四种情况。

如下图:


针对这种情况,设计算法:

    首先,改变z的父节点和兄弟节点,祖父节点为相反的颜色,即color[p(z)]=black,color[p(p(z))]=red, color[w]= black,同时让z指向他的祖父节点。拿第一种图做说明,如图所示:



此时,分两种情况讨论
1.如果z是根节点,直接设置color[z]=black,算法结束
2.如果z不是根节点,那么他的父节点会有红黑两种情况:
2.1如果color[p(z)]=black,算法就结束,红黑树性质调整完毕。
2.2如果color[p(z)]=red,那么根据z与p(z)的左右子树关系,分为两种情况,如图:

不管哪种情况,如果p(z)是根节点,直接color[p(z)]=black,调整完毕。

所以我们讨论的是p(z)不是根节点,p(z)是有父节点的,所以图更新如下:
我们先不关心这个A节点与B节点的左右关系。


我们先讨论第二种情况。因为第一种情况会经第一种演变出来。

首先将z指到他的父节点,然后做左旋。如图:


经过这样的左旋转换,这样就到了我们上面所讨论的第一种情况了。

然后我们讨论第一种情况的处理方案,我们做如下变更,如图所示:

首先设置color[p(z)]=black, color[p(p(z))]=red,然后以p(p(z))位锚点,做右旋。这样就完成了整个树的调整。

梳理一下我们的逻辑过程:


删除一个节点

首先思考一下二叉查找树的删掉算法
删除一个节点,按照节点是否有左右子树,可以分为以下几种情况
1.删掉的节点左右子树都存在
2.删除的节点只有左子树
3.删除的节点只有右子树
4.删除的节点无子树

如下图所示,不同情况的Z节点


删掉的节点左右子树都存在

就用上图分析,假如删除的是B节点,即左右子树都存在,那么算法应该是这样的:
首先沿着B节点的右子树寻找最小的节点x。
然后将最小节点x的值赋值到B节点处,删除x,x如果有右子树,保持x子树的关系。
在这幅图中,就是沿着B的右子树的左子树,寻找到了F这个节点,然后将F的值赋值给B,S节点保持在F节点的父节点N的左子树上。
也就是说我们实际上删除的是F,而不是B,只不过B节点的值改变而已。
这种情况下,就会归结到下面两种场景了。
删除的节点只有右子树
删除的节点无子树
如下图所示过程:



删除的节点只有左子树

将N节点的左子树的父节点设置为N节点的父节点D,判断D与N的左右关系,将F设置为D的左/右子树。如图


删除的节点只有右子树

逻辑类似上面,不在赘述。


删除的节点无子树

直接删除。


红黑树的删除操作是需要在上述算法的基础上进行红黑树调整,以达到保持红黑树的性质。

针对上述四种情况,删除的节点分别是F,N,P,K。在一个红黑树中,如果这些节点的颜色都是红色,他会违背红黑树性质吗?复习一下红黑树性质

1.所有节点都是有颜色的,red or black

2.root节点是black

3.叶节点都是black

4.red节点的左右子节点的颜色必须是black

5.从任何一个内部节点(包括根节点)开始,到任何一个叶节点,所有路径上的黑色节点数量相等,计算黑色节点时,不包括起始节点。

 

 

第1,3,5性质不会违背。

分析剩下的性质在何种场景下会违背:

1.      当删除的节点是root节点,并且它只要一个红色的后续节点,那么就会违背性质2,但是root节点不可能是红色的节点,所以删除红色节点不可能违反性质2。

2.      当删除的节点的是红色节点,那么它的父节点肯定是black,它的子节点肯定是black,,所以也没有问题。

 

综上所述,如果删除的是红色的节点,不用做任何处理。

所以只需要处理删除的节点color是black的情况。而且根据我们的对二叉查找树的删除操作算法的分析,最后被真正删除的节点无非就三种情况
删除的节点只有左子树
删除的节点只有右子树
删除的节点无子树
而且删除的节点是黑色的,那么他们的父节点的颜色呢?兄弟节点的颜色呢?我们逐一分析。

我们对上面这个图进行分析,颜色不确定的节点用黄色代替。

假设D节点是我们要删除的节点,用y表示。y的子树用x表示,x可能为空节点,即y没有子树。

如果x是空节点,那么图就应该是这样的

在这种情况下,删除y,

如果B是黑色的,那么将C直接变成red即可

如果B是红色,那么以B作为锚点,进行左旋,即可。


继续分析x不为空的情况:

如果X是红色的情况下,删除了y,我们只需将y的黑色让x继承,即X的节点由红色变成黑色即可。



上面的情况都很好处理,需要我们认真分析的情况是X是黑色,Y是黑色的情况。看图:


B的颜色,C的颜色,B与y的左右关系都是我们需要分析的。

对于右边的结果,可以有两种场景:

1.C如果是red,那么B只能是black

2.C如果是black,那么B的颜色黑红都可以


就在这两种情况下分析,穷举所有情况

C如果是red,那么B只能是black

w指向C,根据红黑树的性质,那么w的后代必须都是黑

对于这种情况,我们将B和C的颜色分别设置为red和black,然后以B为锚点进行左旋,w重新指向x父节点的另外一个子节点。过程如图所示:

经过这样的转换,最后的结果其实也是第二种情况的一个子情况。

C如果是black,那么B的颜色黑红都可以


因为B(X的父节点)的颜色不确定,所以我们暂时先不关心B,以C(用w表示,X的兄弟节点)为切入点进行分析,根据w的子节点的情况可以分为如上图所示的4种情况,那么就对上述4中情况逐一分析。

Case2.1

将w的颜色变成红色,x指向他的父节点B。此时分析B的颜色:

1.如果B是黑色,算法执行完毕,调整完毕

2.如果B是红色,设置B的颜色为黑色即可。调整完毕



Case2.2与 Case2.4

其实就是w的右子树的颜色为red的情况

我们执行如下操作:

Color[w]=color[p(x)]

Color[p(x)]=black

Color[right[w]]=black

然后以B为锚点进行左旋。

 

具体的解释:

1.设置w的颜色为B的颜色

2.B的颜色设置为黑色

3.w的右子树的颜色设置为黑色

由于B的颜色不确定,经过上述操作后,w的颜色也不确定,但是我们不必关系。

4. 以B为锚点进行左旋。

因为我们使用要保持树的根节点为黑色,这个时候需要进行最后一步的处理,将树根节点设置为黑色即可。


Case2.3

即w的左子树是红色,右子树为黑色。

进行如下操作:

w 设置为 红色

w的左子树设置为黑色

以w为锚点进行右旋

如图所示


分析最后的结果,又进入了case2.2和2.4的情况。







0 0
原创粉丝点击