红黑树插入操作

    原本是在一张纸上画出了红黑树插入操作的所有情况的演变图示,但前两天将删除操作写到博客上,因此想来,索性就将插入操作也一并写上,以免日后图纸丢失,也方便大伙共同研究,学习。

 

红黑树的插入操作

 

1:节点命名约定

N表示新添加的节点。即:取 New 的首字母;

P 表示父节点。即:取 Parent 的首字母;

S表示兄弟姐妹节点。即:取 Sibling的首字母;

G表示祖父节点。即:取 Grandfather的首字母;

Nil表示叶子节点。即所谓的空节点;注意:红黑树中的叶子节点与其他树中所述说的叶子节点不是同一概念。而且红黑树中的叶子节点(即:Nil节点)永远是被定义为黑色的。

 

2:插入操作宏观分析

对于二叉搜索树的插入操作总是插入在某个叶子节点处(注意:此处的叶子并非指rb-treeNil叶子节点,而是真实的叶子节点),而rb-tree的插入也不例外。另外,rb-tree的插入操作我们总是以红色节点插入,如此我们可以保证rb-tree的黑节点数的任何分支上的平衡。当然以红色节点插入,就有可能违背任何父子节点不能同时为红的性质,所以此时需要额外做一些调整处理。

因此,在红黑树中,插入一个节点往大的说,只有以下几种情况:

情况一:红NP;(其中N又分为在P的左子树插入以及右子树插入)

情况二:红NPS;(其中N又分为在P的左子树插入以及右子树插入)

情况三:红NPS;(其中N又分为在P的左子树插入以及右子树插入)

 

其中情况一,插入后的rb-tree的任何性质都没有被破坏,因此不需要做任何调整处理。情况二和情况三插入后均违背了任何父子节点不能同时为红的性质,所以下面我们将全面讨论如何通过调整,重新让最终的rb-tree合法化。

 

3:红黑树插入后平衡处理

在具体分析之前,再次列出红黑树的定义:

1)       任何一个节点非红即黑;

2)       树的根为黑色;

3)       叶子节点为黑色(注意:红黑树的所有叶子节点都指的是Nil节点)

4)       任何两个父子节点不可能同时为红色;

5)       任何节点到其所有分枝叶子的简单路径上的黑节点个数相同;

 

    下面是几个图示说明:

 

 

根据前面的分析,插入节点的所有情况罗列如下:

 

a)       NP,且N为左子节点插入

分析:该情况则新插入的节点N,在插入后不会破坏任何性质,因此插入结束。此时,我们还可推测P的右子树,要么是一个红色节点要不就是一个Nil节点。

 

 

b)       NP,且N为右子节点插入

 

    分析:该情况则新插入的节点N,在插入后不会破坏任何性质,因此插入结束。此时,我们还可推测P的左子树,要么是一个红色节点要不就是一个Nil节点。

 

 

c)       NPS,且N为左子节点插入

根据rb-tree的性质,P为红,则P的父节点,即:对N来说就是G节点,必为黑色节点,否则违背性质4。再根据性质5,则我们可以推测出此时的S节点,也必为Nil节点。否则经过G节点的两个分支的黑节点数将不平衡,即原rb-tree就已经不平衡。

 

分析:插入后,明显的G的左子树深度变为2,右子树却为0(注意:此时SNil节点),因此需要以G节点进行一次右旋转。旋转后再将PG的颜色变换,此时原本经过G的节点的任何路径黑节点数为2,现在经过P节点的任何路线的黑节点数也为2,此时所有性质都满足:

操作:

ð  G右旋转

ð  P由黑色改为红色

ð  G改为红色,此时整棵树已完全满足rb-tree的所有性质。

说明:其实这种情况就是AVL树的LL插入的情况。

 

d)       NPS,且N为右子节点插入。

 

这种情况下,其实我们可以通过一次旋转操作,转变成前面的c)状态。因为对于红P而言,现在新插入的红N位置,在未插入N时,该位置必为Nil节点。因此,P的左子节点也必为Nil节点(不明白的同学,请看性质)。因此,在插入N后,我们可以简简单单地对P做一次左旋转,就转变成了c)情况。只是旋转后的P节点相当于c)中的N,即:此时将P作为新插入的节点,再进行调整即可。

 

分析:经过上面左旋转后,此时将P节点当作是新插入的节点”N”,再转向c)情况处理即可。

操作:

ð  P左旋转

ð  以旋转后的P节点作为新插入的节点”N”

ð  转向c)情况处理。

说明:这种情况其实就是AVL中的LR插入操作。

 

e)       NPS,且N为左子节点插入

 

根据rb-tree的性质,红PS,则G必为黑。因此,原先经过G节点的黑节点数必为2。且原先的PS的左、右子节点必都为Nil

 

分析:旋转前,原本经过G节点的黑节点数为2。旋转后,原本经过G的节点却变成经过P节点。而经过P的右分支的黑节点数为2不变,但经过P的左子树的黑节点数却变为1,减少了1,因此,直接将新插入的节点N由红色改为黑色。如此,不论是原本经过G的还是现在经过P的,黑节点数都是平衡的。但由于原本的G位置是黑色的,现在却变成红色。因此,可以将旋转后的P节点当成是新插入的节点,再转向a)情况处理即可。(即:所谓的上溯)。

操作:

ð  G右旋转

ð  N节点由红色改为黑色

ð  以此时的P节点作为新插入的节点”N”,转向a)情况处理即可。

说明:这种情况就是AVL中的LL插入操作。

 

f)       NPS,且N为右子节点插入

 

根据rb-tree的性质,红PS,则G必为黑。因此,原先经过G节点的黑节点数必为2。且原先的PS的左、右子节点必都为Nil。并且有了前面的e)点分析,其实不难想到,我们可以将f)情况通过简简单单的一个左旋转操作,将问题变换为e)情况。

 

分析:将P左旋转后,此时将旋转后的P节点当成是将插入的节点”N”,则情况就跟e)点完全一样。

操作:

ð  P左旋转;

ð  P作为新插入的节点”N”

ð  转向e)情况处理即可。

说明:这种情况就是AVL中的LR插入操作。

 

至此,红黑树的L型(即:LLLR)插入全部介绍完毕。对于R型(即:RLRR)插入的情况,仅仅只是上面b)c)d)e)f)情况中的旋转方向相反一下即可。思路完全相同。另外,与红黑树的删除操作相比,插入操作显得非常的直观,没有删除操作那么复杂。其实将上述的a)f)情况绘制在一张纸上的话,一张纸就可以完全说明清楚以上全部情况。(有兴趣的同学可以试下看)。

另外,还有一个小细节需要注意,如果在上溯的过程中,如果已经到了树的根了,即:如上面的e)情况中,则此时直接将节点P改为黑色即可。因为已经到根了,P即为根节点。根据性质2,根节点必为黑色。并且此时将P改为黑色,则rb-tree的任何性质都不会被破坏,插入结束。

 

Q&A

前面e)情况中,为什么要旋转再变色,而不直接改变G为红色,改变P为黑色,改变S为黑色,然后再将G为新插入的节点转到a)情况处理?

 

ð  个人的理解:要这么转换也是可以的,但从操作步骤数上,将比上文中e)的操作来的多,因此花的时间也将更多。

 

以上为个人理解,有错误之处,欢迎指正!

posted @ 2016-05-07 14:03  Jacc.Kim  阅读(876)  评论(0编辑  收藏  举报