红黑树

红黑树


2019-02-14

红黑树是什么

和上篇介绍的 AVL 树一样,红黑树也是一种自平衡的二叉查找树,通过之前的学习我们知道,自平衡的二叉查找树是高效的,它可以在时间复杂度为img下做查找、插入和删除。红黑树具备如下性质:

  1. 节点非红即黑(空节点和根节点为黑色)
  2. 不能出现连续两个红色节点
  3. 任意节点到空节点的路径需包含数量相同的黑色节点

下面是红黑树的例子:



红黑树复杂的地方主要在于对其执行增删操作会破坏上面的红黑属性,因此要有相应的机制恢复。这里的恢复机制除了在介绍 AVL Tree 时讲到的旋转,还多了一种重新着色,顾名思义,把红色变成黑色,黑色变成红色即可,在下面的介绍中会具体讲到。

红黑树的插入

先看下红黑树插入的情况,红黑树的插入思路是先按照二叉查找树插入的操作(具体思路请看这篇)插入节点,再通过重新着色或者旋转的方法恢复红黑属性。规定了新增的节点颜色为红色(如果设为黑色,就会导致根到叶子的路径上有一条路上,多一个额外的黑节点,这个是很难调整的。)但是设为红色节点后,可能会导致出现两个连续红色节点的冲突,因此插入操作主要是解决上面的红黑属性 2 。下面为总结的 4 种插入情景及对应调整 “套路”,插入时检查叔叔的颜色,假设 z 为新插入的节点,

  1. z 为根节点 → 将 z 置为黑色
  2. z 的叔叔是红色 → 重新着色
  3. z 的叔叔是黑色(直线形)→ 旋转 z 的爷爷 & 重新着色
  4. z 的叔叔是黑色(三角形) → 旋转 z 的父亲

基于这 4 点举例分析红黑树插入的情况,先插入节点 15:



由于 15 是根节点,满足套路 1 ,因此将其由红色置为黑色;再相继插入 5、1:



插入节点 1 后满足套路 3 ,这里先对节点 15 右旋,再重新着色即恢复红黑属性。

再看下一个稍微复杂的例子,对下面这棵红黑树插入节点 10:




插入节点 10 后满足套路 2 ,将 9 、13 置为黑色,将 12 置为红色,流程如下:



此时满足套路 4,将父节点 15 右旋:



此时满足套路 3,左旋爷爷节点 8 ,再对 8 和 12 重新着色即恢复红黑属性。



红黑树的删除

与插入相同,红黑树的删除思路是先按照二叉查找树删除的操作(具体思路请看这篇)删除节点,再通过重新着色或者旋转的方法恢复红黑属性。插入操作主要是违反了上面提到的红黑属性 2 ,而删除操作是违反了红黑属性 3 。在插入操作中,根据叔叔的颜色来选择适当的 “套路” ,在删除操作中,主要依据兄弟的颜色 。设 X 是要删除的节点,Y 是替换树中 X 的节点,S 为 X 的兄弟。

I. X 和 Y 中有一个为红色 -> 将 Y 置为黑色

来看下例子,在下面的红黑树中要删除节点 30 :




首先通过二叉查找树的遍历方法找到 30 ,遍历右子树,用 35 代替,再删除 35 ,此时 X 是红色节点 35 ,Y 是空节点(本来就是黑色),直接删除 35 ,流程如下:



再看下面这个例子,在下面的红黑树中要删除节点 30 :




首先通过二叉查找树的遍历方法找到 30 ,遍历右子树,用 32 代替,再删除 32 ,此时 X 是黑色节点 32 ,Y 是红色节点 35 ,因此将节点 32 删除,并将节点 35 置为黑色,流程如下:



II. X 和 Y 都是黑色。

接下来的情景比较特殊,引入双黑色的概念。双黑色是指:当黑色节点被删除并被黑色孩子替换时,孩子被标记为双黑色。被标记为双黑色的节点,所在的路径黑色节点数量加 1 。共有以下这么几种情况:

a) 兄弟 S 是黑色,S 的孩子至少有一个是红色 ,假设 R 是 S 的红色子节点,根据 R 和 S 的位置可以分为下面四种情况:

a1 . 右右:S 是其父亲的右节点,R 是 S 的右节点。



a2 . 右左:S 是其父亲的右节点,R 是 S 的左节点。




a3 . 左左:S 是其父亲的左节点,R 是 S 的左节点。



a4 . 左右:S 是其父亲的左节点,R 是 S 的右节点。



举个例子,在下面的红黑树中删除节点 5 :



删后如下:




满足 a1 ,左旋根节点 10 ,并对 40 重新着色,流程如下:



b) 兄弟 S 是黑色,S 的孩子都是黑色。解决方案:如果父节点为黑色,则置为双黑色,;如果父节点为红色,则置为黑色;S 置为红色;若根节点为双黑色时,置为黑色。



举个例子,在下面的红黑树中删除节点 10 :



首先通过二叉查找树的遍历方法找到 10 ,遍历右子树,用 20 代替,再删除 20 ,删完如下:



上面的图偷懒了,我们知道空节点默认是黑色的,所以完整的图应该是这样的:



此时不满足红黑属性 3 ,从根节点 10 到其他空叶子节点经过的黑色节点数量为 3 ,而到 z 这条路径经过的黑色节点数量为 2 ,将 z 置为双黑色,这样就平衡了:



接下来就是解决双黑色的问题了,将 38 置为红色,将 30 置为黑色即恢复红黑树 :



下面的红黑树中删除节点 10



删后如下:



将 40 置为红色,并把 30 置为双黑色:



由于节点 30 是根节点,所以直接去掉节点 30 外圈加的黑色:



c) 兄弟 S 是红色。按照 S 的位置又分为下面两种情况

c1. 右侧:S 是右子节点:



c2. 左侧:S 是左子节点:



Enjoy –☺