红黑树 RBT (Red Black Tree)

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

用途和好处

红黑树和AVL树一样都对插入时间、删除时间和查找时间提供了最好可能的最坏情况担保。这不只是使它们在时间敏感的应用如实时应用(real time application)中有价值,而且使它们有在提供最坏情况担保的其他数据结构中作为建造板块的价值;例如,在计算几何中使用的很多数据结构都可以基于红黑树。

红黑树在函数式编程中也特别有用,在这里它们是最常用的持久数据结构之一,它们用来构造关联数组集合,在突变之后它们能保持为以前的版本。除了O(log n)的时间之外,红黑树的持久版本对每次插入或删除需要O(log n)的空间。

红黑树是  2-3-4树的 一种等同。换句话说,对于每个 2-3-4 树,都存在至少一个数据元素是同样次序的红黑树。在 2-3-4 树上的插入和删除操作也等同于在红黑树中颜色翻转和旋转。这使得 2-3-4 树成为理解红黑树背后的逻辑的重要工具,这也是很多介绍算法的教科书在红黑树之前介绍 2-3-4 树的原因,尽管 2-3-4 树在实践中不经常使用。


性质

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

性质1. 节点是红色或黑色

性质2. 根是黑色。

性质3. 所有叶子都是黑色(叶子是NIL节点)。

性质4. 每个红色节点的两个子节点都是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色节点)

性质5. 从任一节点到其每个叶子的所有简单路径都包含相同数目的黑色节点。

操作

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

插入

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

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

在下面的示意图中,将要插入的节点标为 NN的父节点标为 PN的祖父节点标为 GN的叔父节点标为 U。在图中展示的任何颜色要么是由它所处情形所作的假定,要么是这些假定所暗含 (imply) 的。

情形1: 新节点 N位于树的根上,没有父节点。在这种情形下,我们把它重绘为黑色以满足性质2 [5]。因为它在每个路径上对黑节点数目增加一,性质5 [4]符合。
情形2: 新节点的父节点 P是黑色,所以性质4 [3]没有失效(新节点是红色的)。在这种情形下,树仍是有效的。性质5 [4]也未受到威胁,尽管新节点 N有两个黑色叶子子节点;但由于新节点 N是红色,通过它的每个子节点的路径就都有同通过它所取代的黑色的叶子的路径同样数目的黑色节点,所以依然满足这个性质。

注意: 在下列情形下我们假定新节点的父节点为红色,所以它有祖父节点;因为如果父节点是根节点,那父节点就应当是黑色。所以新节点总有一个叔父节点,尽管在情形4和5下它可能是叶子节点。

情况 3 示意图

情形3: 如果父节点 P和叔父节点 U二者都是红色,(此时新插入节点N做为P的左子节点或右子节点都属于情形3,这里右图仅显示N做为P左子的情形)则我们可以将它们两个重绘为黑色并重绘祖父节点 G为红色(用来保持性质5 [4])。现在我们的新节点 N有了一个黑色的父节点 P。因为通过父节点 P或叔父节点 U的任何路径都必定通过祖父节点 G,在这些路径上的黑节点数目没有改变。但是,红色的祖父节点 G的父节点也有可能是红色的,这就违反了性质4 [3]。为了解决这个问题,我们在祖父节点 G上递归地进行 情形1的整个过程。(把 G当成是新加入的节点进行各种情况的检查)

注意: 在余下的情形下,我们假定父节点P 是其父亲G 的左子节点。如果它是右子节点,情形4情形5中的应当对调。

情况 4 示意图
情形5: 父节点 P是红色而叔父节点 U 是黑色或缺少,新节点 N 是其父节点的左子节点,而父节点 P又是其父节点 G的左子节点。在这种情形下,我们进行针对祖父节点 G 的一次 右旋转; 在旋转产生的树中,以前的父节点 P现在是新节点 N和以前的祖父节点 G 的父节点。我们知道以前的祖父节点 G是黑色,否则父节点 P就不可能是红色 (如果  P 和  G 都是红色就违反了性质4,所以  G 必须是黑色)。我们切换以前的父节点 P和祖父节点 G的颜色,结果的树满足性质4 [3]。性质5 [4]也仍然保持满足,因为通过这三个节点中任何一个的所有路径以前都通过祖父节点 G ,现在它们都通过以前的父节点 P。在各自的情形下,这都是三个节点中唯一的黑色节点。
 注意插入实际上是 原地算法,因为上述所有调用都使用了 尾部递归

删除

如果需要删除的节点有两个儿子,那么问题可以被转化成删除另一个只有一个儿子的节点的问题(为了表述方便,这里所指的儿子,为非叶子节点的儿子)。对于二叉查找树,在删除带有两个非叶子儿子的节点的时候,我们找到要么在它的左子树中的最大元素、要么在它的右子树中的最小元素,并把它的值转移到要删除的节点中(如在这里所展示的那样)。我们接着删除我们从中复制出值的那个节点,它必定有少于两个非叶子的儿子。因为只是复制了一个值而不违反任何属性,这就把问题简化为如何删除最多有一个儿子的节点的问题。它不关心这个节点是最初要删除的节点还是我们从中复制出值的那个节点。

在本文余下的部分中,我们只需要讨论删除只有一个儿子的节点(如果它两个儿子都为空,即均为叶子,我们任意将其中一个看作它的儿 子)。如果我们删除一个红色节点(此时该节点的儿子将都为叶子节点),它的父亲和儿子一定是黑色的。所以我们可以简单的用它的黑色儿子替换它,并不会破坏 属性3和4。通过被删除节点的所有路径只是少了一个红色节点,这样可以继续保证属性5。另一种简单情况是在被删除节点是黑色而它的儿子是红色的时候。如果 只是去除这个黑色节点,用它的红色儿子顶替上来的话,会破坏属性5,但是如果我们重绘它的儿子为黑色,则曾经通过它的所有路径将通过它的黑色儿子,这样可 以继续保持属性5。

需要进一步讨论的是在要删除的节点和它的儿子二者都是黑色的时候,这是一种复杂的情况。我们首先把要删除的节点替换为它的儿子。出于方便,称呼这个儿子为 N(在新的位置上),称呼它的兄弟(它父亲的另一个儿子)为 S

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

情况 1: N 是新的根。在这种情况下,我们就做完了。我们从所有路径去除了一个黑色节点,而新根是黑色的,所以属性都保持着。

注意: 在情况2、5和6下,我们假定 N 是它父亲的左儿子。如果它是右儿子,则在这些情况下的应当对调。

情况 2 示意图

情况 2: S 是红色。在这种情况下我们在N的父亲上做左 旋转,把红色兄弟转换成N的祖父。我们接着对调 N 的父亲和祖父的颜色。尽管所有的路径仍然有相同数目的黑色节点,现在 N 有了一个黑色的兄弟和一个红色的父亲,所以我们可以接下去按 4、5或6情况来处理。(它的新兄弟是黑色因为它是红色S的一个儿子。)

情况 3 示意图

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


情况 4: S 和 S 的儿子都是黑色,但是 N 的父亲是红色。在这种情况下,我们简单的交换 N 的兄弟和父亲的颜色。这不影响不通过 N 的路径的黑色节点的数目,但是它在通过 N 的路径上对黑色节点数目增加了一,添补了在这些路径上删除的黑色节点。
情况 4 示意图

情况 5 示意图

情况 5: S 是黑色,S 的左儿子是红色,S 的右儿子是黑色,而 N 是它父亲的左儿子。在这种情况下我们在 S 上做右旋转,这样 S 的左儿子成为 S 的父亲和 N 的新兄弟。我们接着交换 S 和它的新父亲的颜色。所有路径仍有同样数目的黑色节点,但是现在 N 有了一个右儿子是红色的黑色兄弟,所以我们进入了情况 6。N 和它的父亲都不受这个变换的影响。

情况 6: S 是黑色,S 的右儿子是红色,而 N 是它父亲的左儿子。在这种情况下我们在 N 的父亲上做左旋转,这样 S 成为 N 的父亲和 S 的右儿子的父亲。我们接着交换 N 的父亲和 S 的颜色,并使 S 的右儿子为黑色。子树在它的根上的仍是同样的颜色,所以属性 3 没有被违反。但是,N 现在增加了一个黑色祖先: 要么 N 的父亲变成黑色,要么它是黑色而 S 被增加为一个黑色祖父。所以,通过 N 的路径都增加了一个黑色节点。

此时,如果一个路径不通过 N,则有两种可能性:

  • 它通过 N 的新兄弟。那么它以前和现在都必定通过 S 和 N 的父亲,而它们只是交换了颜色。所以路径保持了同样数目的黑色节点。
  • 它通过 N 的新叔父,S 的右儿子。那么它以前通过 S、S 的父亲和 S 的右儿子,但是现在只通过 S,它被假定为它以前的父亲的颜色,和 S 的右儿子,它被从红色改变为黑色。合成效果是这个路径通过了同样数目的黑色节点
File:Red-black tree delete case 6.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值