红黑树详解

红黑树

我们知道二叉查找树有较高的查找速度,同时为了避免单枝树等情况,又希望该二叉树接近于满二叉树,或者二叉树的每一个节点的左、右子树深度尽量相等,这样的二叉树称为平衡二叉树。平衡二叉树的常用实现方法有红黑树、AVL、替罪羊树、Treap、伸展树等。接下来我们看一下红黑树的原理。

定义

红黑树(英语:Red–black tree)是一种自平衡二叉查找树,是在计算机科学中用到的一种数据结构,典型的用途是实现关联数组。它在1972年由鲁道夫·贝尔发明,被称为"对称二叉B树",它现代的名字源于Leo J. Guibas和Robert Sedgewick于1978年写的一篇论文。红黑树的结构复杂,但它的操作有着良好的最坏情况运行时间,并且在实践中高效:它可以在O(logN)时间内完成查找,插入和删除,这里的N是树中元素的数目。

性质

红黑树是每个节点都带有颜色属性的二叉查找树,颜色为红色或黑色。在二叉查找树强制一般要求以外,对于任何有效的红黑树我们增加了如下的额外要求:

  1. 节点是红色或黑色。
  2. 根是黑色。
  3. 所有叶子都是黑色(叶子是NULL节点)。
  4. 每个红色节点的子节点一定是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色节点。)
  5. 从任一节点到其每个叶子的所有简单路径都包含相同数目的黑色节点。

这些约束确保了红黑树的关键特性:从根到叶子的最长的可能路径不多于最短的可能路径的两倍长。结果是这个树大致上是平衡的(红黑树是近似平衡的二叉排序树)。因为操作比如插入、删除和查找某个值的最坏情况时间都要求与树的高度成比例,这个在高度上的理论上限允许红黑树在最坏情况下都是高效的,而不同于普通的二叉查找树。

要知道为什么这些性质确保了这个结果,注意到性质4导致了路径不能有两个毗连的红色节点就足够了。最短的可能路径都是黑色节点,最长的可能路径有交替的红色和黑色节点。因为根据性质5所有最长的路径都有相同数目的黑色节点,这就表明了没有路径能多于任何其他路径的两倍长

在很多树数据结构的表示中,一个节点有可能只有一个子节点,而叶子节点包含数据。用这种范例表示红黑树是可能的,但是这会改变一些性质并使算法复杂。为此,本文中我们使用"nil叶子"或"空(null)叶子",它不包含数据而只充当树在此结束的指示。这些节点在绘图中经常被省略,导致了这些树好像同上述原则相矛盾,而实际上不是这样。与此有关的结论是所有节点都有两个子节点,尽管其中的一个或两个可能是空叶子。

如下图所示就是一颗典型的红黑树:
红黑树详解_第1张图片

因为每一个红黑树也是一个特化的二叉查找树,因此红黑树上的只读操作与普通二叉查找树上的只读操作相同。然而,在红黑树上进行插入操作和删除操作会导致不再匹配红黑树的性质。恢复红黑树的性质需要少量(log n))的颜色变更(实际是非常快速的)和不超过三次树旋转(对于插入操作是两次)。虽然插入和删除很复杂,但操作时间仍可以保持为(log n)次。

插入

我们首先以二叉查找树的方法增加节点并标记它为红色。(如果设为黑色,就会导致根到叶子的路径上有一条路上,多一个额外的黑节点,这个是很难调整的。但是设为红色节点后,可能会导致出现两个连续红色节点的冲突,那么可以通过颜色调换(color flips)和树旋转来调整。)下面要进行什么操作取决于其他临近节点的颜色。同人类的家族树中一样,我们将使用术语叔父节点来指一个节点的父节点的兄弟节点。注意:

  • 性质1和性质3总是保持着。
  • 性质4只在增加红色节点、重绘黑色节点为红色,或做旋转时受到威胁。
  • 性质5只在增加黑色节点、重绘红色节点为黑色,或做旋转时受到威胁。

插入操作总体可分为以下5中情况:
注意:图中节点 I表示插入节点,P表示父节点,G表示祖父节点,U表示叔父节点。

case1: 空树

只需将该节点设置为根节点,同时颜色置为黑色。
红黑树详解_第2张图片

case2: 插入节点的父节点P是黑色

由于新插入节点的父节点是黑色,对于红黑树的性质没有影响固无需做相应的调整
红黑树详解_第3张图片

case3: 插入节点的父节点和叔父节点都是红色

注意下面是对称结构,故做同一情况处理。
红黑树详解_第4张图片
新插入节点后不满足性质4红色节点不能连续。则我们可以将父节点P和叔父节点U设为黑色并重绘祖父节点G设为红色(用来保持性质5)。更改后如下图所示
红黑树详解_第5张图片
现在父节点P变为了黑色。因为通过父节点P或叔父节点U的任何路径都必定通过祖父节点G,在这些路径上的黑节点数目没有改变(性质5)。但是,红色的祖父节点G可能是根节点,这就违反了性质2,也有可能祖父节点G的父节点是红色的,这就违反了性质4。为了解决这个问题,我们在祖父节点G上递归地进行调整。(把G当成是新加入的节点进行各种情形的检查)。

case4:父节点P是红色而叔父节点U是黑色或没有(null节点),并且新节点I是其父节点的左孩子,而父节点P又是其父节点G的左孩子。

如下图所示,这里对称结构就没有给出来了。
红黑树详解_第6张图片
这里我们将图中的情况用null节点补以便观察整个调整过程:

  1. 将父节点P设置为黑色
  2. 将祖父节点G设置为红色
    红黑树详解_第7张图片
  3. 对G进行右旋
    红黑树详解_第8张图片

case5: 父节点P是红色而叔父节点U是黑色或没有(null节点),并且新节点I是其父节点P的右子节点而父节点P又是其父节点G的左子节点

如下图所示:
红黑树详解_第9张图片
调整方法:

  1. 将P进行左旋
  2. 然后将P作为当前节点
  3. 变为case4,根据case4继续调整
    在这里插入图片描述
    红黑树详解_第10张图片
    到这里整个插入的情况就说完了,以上各个情况可能会在调整过程中由一种场景切换到另一种场景。

删除

对于二叉查找树,在删除带有两个非叶子儿子的节点的时候,我们要么找到它左子树中的最大元素、要么找到它右子树中的最小元素,并把它的值转移到要删除的节点中(如在这里所展示的那样)。我们接着删除我们从中复制出值的那个节点( 在"被删除节点"有两个非空子节点的情况下,它的替换节点不可能是双子非空)。因为只是复制了一个值(没有复制颜色),不违反任何性质,这就把问题简化为如何删除最多有一个儿子的节点的问题。它不关心这个节点是最初要删除的节点还是我们从中复制出值的那个节点。

如果使用标签m表示要删除的节点;c表示m的选定替换M的子节点,我们也将c其称为“子节点”

  1. 如果我们删除一个红色节点M(此时该节点的儿子将都为叶子节点),我们只是简单的用C替换M,根据性质4,C必定是黑色(这只能在m有两个叶子节点时发生,因为删除的节点最多只有一个非空儿子,如果红色节点m的一侧有一个黑色的非空叶子节点,而另一侧有一个叶子节点,则两侧的黑色节点计数将不同,因此将违反性质5)。所以我们可以简单的用它的黑色儿子替换它,并不会破坏性质3和性质4。通过被删除节点的所有路径只是少了一个红色节点,这样可以继续保证性质5。
  2. 另一种简单情况是M为黑色,C为红色。如果只是去除这个黑色节点,用它的红色儿子顶替上来的话,会破坏性质5,但是如果我们重绘它的儿子为黑色,则曾经通过它的所有路径将通过它的黑色儿子,这样可以继续保持性质5
  3. 复杂的情况是M和C都是黑色的。(这种情况下该结点的两个儿子都是叶子结点,否则若其中一个儿子是黑色非叶子结点,另一个儿子是叶子结点,那么从该结点通过非叶子结点儿子的路径上的黑色结点数最小为2,而从该结点到另一个叶子结点儿子的路径上的黑色结点数为1,违反了性质5)。

我们首先把要删除的节点M替换为它的儿子C,在将在新的位置上的C称为N,称呼它的兄弟(它父亲的另一个儿子)为S。在下面的示意图中,我们还是使用P称呼N的父亲,SL称呼S的左儿子,SR称呼S的右儿子。

如果N和它初始的父亲是黑色,则删除它的父亲导致通过N的路径都比不通过它的路径少了一个黑色节点。因为这违反了性质5,树需要被重新平衡。有几种情形需要考虑:

case1:N是新的根。

在这种情形下,删除原节点后我们就做完了。我们从所有路径去除了一个黑色节点,而新根是黑色的,所有性质都保持着。

case2: S是红色。

在这种情形下我们在N的父亲上做左旋转,把红色兄弟S转换成N的祖父,我们接着对调N的父亲和祖父的颜色。完成这两个操作后,尽管所有路径上黑色节点的数目没有改变,但是我们删除了一个黑色节点,此时通过原来删除的节点的黑色路径长度少1,所以我们需要接下去按case4、case5或case6来处理。
红黑树详解_第11张图片

case3: N的父亲、S和S的儿子都是黑色的。

在这种情形下,我们简单的重绘S为红色。结果是通过S的所有路径,它们就是以前不通过N的那些路径,都少了一个黑色节点。因为删除N的初始的父亲使通过N的所有路径少了一个黑色节点,这使事情都平衡了起来。但是,通过P的所有路径现在比不通过P的路径少了一个黑色节点,所以仍然违反性质5。要修正这个问题,我们要从case2开始,在P上做重新平衡处理。

红黑树详解_第12张图片

case4: S和S的儿子都是黑色,但是N的父亲是红色。

在这种情形下,我们简单的交换N的兄弟和父亲的颜色。这不影响不通过N的路径的黑色节点的数目,但是它在通过N的路径上对黑色节点数目增加了一,添补了在这些路径上删除的黑色节点。
红黑树详解_第13张图片

case5: S是黑色,S的左儿子是红色,S的右儿子是黑色,而N是它父亲的左儿子。

在这种情形下我们在S上做右旋转,这样S的左儿子成为S的父亲和N的新兄弟。我们接着交换S和它的新父亲的颜色。所有路径仍有同样数目的黑色节点,但是现在N有了一个黑色兄弟,他的右儿子是红色的,所以我们进入了case6。N和它的父亲都不受这个变换的影响。
红黑树详解_第14张图片

case6: S是黑色,S的右儿子是红色,而N是它父亲的左儿子。

在这种情形下我们在N的父亲上做左旋转,这样S成为N的父亲P和S的右儿子的父亲。我们接着交换N的父亲和S的颜色,并使S的右儿子为黑色。子树在它的根上的仍是同样的颜色,所以性质3没有被违反。现在通过N的路径都增加了一个黑色节点,弥补了被删除的节点,同时新祖父节点的颜色不变,所以通过N的路径的黑色节点数目与删除节点前保持不变。

此时,不通过N的路径(变化之前通过P右子树即通过S的左右子树)则有两种可能性:

  • 它通过N的新兄弟(以前S的左孩子)。那么它以前和现在都必定通过P和S,而它们只是交换了颜色。所以路径保持了同样数目的黑色节点。
  • 它通过N的新叔父(以前/现在S的右孩子),S的右儿子SR。那么它以前通过P、S和SR,现在通过S和SR。同样变化前后通过了同样数目的黑色节点。

所以在任何情况下,在这些路径上的黑色节点数目都没有改变。所以我们恢复了性质4。在示意图中的白色节点可以是红色或黑色,但是在变换前后都必须指定相同的颜色。
红黑树详解_第15张图片

你可能感兴趣的:(算法)