参考:

面试问你红黑树,可以这样回答

红黑树详解,对插入旋转独到理解

红黑树变色和旋转,图文案例讲解

红黑树简介及左旋、右旋、变色

1. 红黑树简介

1.1 为什么要使用红黑树

红黑树是一种自平衡的二叉查找树。

对于二叉查找树,容易出现严重单边长的情况,会导致查询效率降低,于是出现了平衡二叉查找树,包含 AVL 树, AVL 树通过旋转达到平衡,平衡因子为 1,过于严格,插入时会导致大量旋转,性能降低,于是出现了红黑树。

红黑树使用两种颜色来达到维持平衡的目的!

1.2 红黑树特性

红黑树有 5 个知名特性

  1. 每个节点或者是黑色,或者是红色。
  2. 根节点是黑色。
  3. 每个叶子节点(NULL)是黑色。 注意:这里叶子节点,是指为空(NULL)的叶子节点!
  4. 如果一个节点是红色的,则它的子节点必须是黑色的。 注意:可以出现父节点和子节点都是黑色
  5. 任意一个节点到其叶子节点的所有路径上包含相同数目的黑节点。

红黑树的最坏情况:最长路径是最短路径的 2 倍。

最坏情况

1.3 平衡手段

对于红黑树的平衡,有两种方法:

  1. 通过旋转,也就是和AVL的旋转一样,左旋和右旋。旋转也是改变颜色的一种方案,主要是达到颜色平衡的目的。
  2. 通过改变节点的颜色,也就是对节点重新着色。

2. 插入节点

2.1 插入的是根节点

直接涂黑即可。

2.2 插入节点的父节点是黑色

img

这个时候插入一个红色的节点并没有对这五个性质产生破坏。所以直接插入不用在进行调整操作。

2.3 插入节点父节点是红色

2.3.1 父红叔红

要插入节点的父节点是红色的,叔叔节点也是红色的,这时候通过变色达到平衡。

img

将父节点和叔叔节点变为黑色,祖父节点变为红色(祖父节点之前肯定为黑色),就可以让此子树达到红黑平衡。路径上黑色节点数量不变。

然后以祖父节点为根据,继续调整。

2.3.2 父红叔黑,插入节点和父节点在同一边

同一边是相当于祖父而言,若父亲位于祖父的左边,插入节点也为祖父的左边,则为同一边。

下图仅展示局部

aa

此时,通过旋转即可解决,如上,对祖父节点进行一次右旋,同时将父节点和祖父节点颜色互换,就变成了

aaa

达到了平衡。

为什么会想到这么做?

上面我们提到解决这个问题的本质就是变色,旋转也是为了更好的变色,变色肯定变的是插入节点的父节点,若父节点变为黑色,则此路径多了一个黑色节点,叔叔也需要多一个黑色结点,但叔叔本来就是黑色的,怎么才能多一个黑色节点呢?

那么我们就可以通过旋转,使父节点路径少一个节点。此时叔叔路径多个一个黑色的祖父节点,变黑即可,即为上述方式。

2.3.3 父红叔黑,插入节点和父节点非同一边

img

先对父节点进行左旋,使之变为同一边的 case,然后按同一边方案处理即可,左旋后如下:

img

3. 删除

通俗易懂的红黑树图解(下)

4. 其它

4.1 插入节点的颜色

为保持平衡,插入节点的颜色初始化为红色,才有可能不破坏平衡。

4.2 变色时,为什么也需要改变祖父节点颜色?

只改变父节点和叔叔节点的颜色不行吗?

如下的左图是红黑树的一个局部,一开始是满足红黑树的特性的,在其中插入了红色节点10,两个红节点连在一起了,不再满足红黑树的特性 4。

img

由于父节点和叔叔节点都为红色,需要变色。

通过变色,先将节点 20 变成黑色,特性 4 满足了,但又不满足特性 5,所以继续将节点 30 变成红色,节点 40 变成黑色。

img

若我们不改变 30 为红色,则对于 30 的父节点来说,这个子树上多了一个黑色节点,破坏了平衡。

经过3次变色后,从局部看,已经重新满足了红黑树的特性。但是,从整棵树来看,还不一定满足红黑树的特性,如果节点 30 的父节点也是红色,则还需要继续对这棵树进行调整。