vlambda博客
学习文章列表

图解:什么是红黑树?(上篇)

图解:什么是红黑树?(上篇)

图解:什么是红黑树?(上篇)

图解:什么是红黑树?(上篇)

图解:什么是红黑树?(上篇)

图解:什么是红黑树?(上篇)

什么是红黑树?

红黑树(Red Black Tree)是一颗自平衡(self-balancing)的二叉排序树(BST),树上的每一个结点都遵循下面的规则(特别提醒,这里的自平衡和平衡二叉树AVL的高度平衡有别):

  1. 每一个结点都有一个颜色,要么为红色,要么为黑色;

  2. 树的根结点为黑色;

  3. 树中不存在两个相邻的红色结点(即红色结点的父结点和孩子结点均不能是红色);

  4. 从任意一个结点(包括根结点)到其任何后代 NULL 结点(默认是黑色的)的每条路径都具有相同数量的黑色结点。

图解:什么是红黑树?(上篇)

这就是一颗典型的红黑树,树中的每个结点的颜色要么是黑色,要么是红色;根结点 6 为黑色结点;树中不存在两个相邻的红色结点,比如结点 15 为红色结点,其父亲节点 6 与两个孩子结点就一定是黑色,而不能是红色;从结点到其后代的 NUll结点 的每条路径上具有相同数目的黑色结点,比如根结点 6 到其左子树的 NULL结点 包含三个黑色结点,到其右子树所有的 NULL 结点也包含三个黑色结点。 可能还不够清晰,为此我对上图做了修改为所有默认为黑色的 NULL 结点给了一个标记。

图解:什么是红黑树?(上篇)

现在解释规则的第四条简直不能再清晰了!比如根结点 6 到 NULL结点 a 的路径 6→2→a 上的黑色结点为 3 个,从根结点 6 到结点 c 的路径 6→15→10→9→c 中包含的黑色结点个数也是 3 个,同理从根结点 6 到其他所有 NULL结点 的黑色结点数都是 3 。再举个栗子,从红色结点 15 到NULL结点 d 的路径 15→18→g 包含 2 个黑色结点,到NULL结点 c 的路径 15→10→9→c 也包含黑色结点 2 个,从结点 15 到其所有后代的 NULL结点的  黑色结点数目都是 2

图解:什么是红黑树?(上篇)

图解:什么是红黑树?(上篇)

图解:什么是红黑树?(上篇)

为什么要有红黑树?

大多数二叉排序树BST的操作(查找、最大值、最小值、插入、删除等等)都是 的时间复杂度,h 为树的高度。但是对于斜树而言(BST极端情况下出现),BST的这些操作的时间复杂度将达到 。为了保证BST的所有操作的时间复杂度的上限为 ,就要想办法把一颗BST树的高度一直维持在 ,而红黑树就做到了这一点,红黑树的高度始终都维持在 ,n 为树中的顶点数目.

图解:什么是红黑树?(上篇)

图解:什么是红黑树?(上篇)

红黑树RBT与平衡二叉树AVL比较:

AVL 树比红黑树更加平衡,但AVL树在插入和删除的时候也会存在大量的旋转操作。所以当你的应用涉及到频繁的插入和删除操作,切记放弃AVL树,选择性能更好的红黑树;当然,如果你的应用中涉及的插入和删除操作并不频繁,而是查找操作相对更频繁,那么就优先选择 AVL 树进行实现。

一颗红黑树是如何保持平衡的?

下面举一个简单但是很经典的例子,包含3个结点的单链是不可能出现在红黑树当中的。 关于这一点,我们可以自己绘制一条单链,然后尝试为其着色,然后判断是否是一颗红黑树证明这一点。

图解:什么是红黑树?(上篇)

从上图中可以发现,将根结点 9 涂黑色,其他结点分四种情况着色,结果都不满足红黑树的性质要求。唯一的办法就是调整树的高度,下面提供两种可行的设计方案:

图解:什么是红黑树?(上篇)

上面的这个例子算是对红黑树维持平衡的初探,接着再看两个重要的概念:

什么是一颗红黑树的黑高(Black Height)?

在一颗红黑树中,从某个结点 x 出发(不包含该结点)到达一个叶结点的任意一条简单路径上包含的黑色结点的数目称为 黑高 ,记为 bh(x)

图解:什么是红黑树?(上篇)

图解:什么是红黑树?(上篇)

图解:什么是红黑树?(上篇)

图解:什么是红黑树?(上篇)

计算结点 6 的黑高,从结点 6 到结点 c 的路径是  6→15→10→9→c  ,其中黑色结点为 6、10、c ,但是在计算黑高时,并不包含结点本身,所以从结点 6 到结点 c 的路径上的黑色结点个数为 2 ,那么 bh(6)=2  ;从结点 15 到结点 c 的路径为 15→10→9→c ,其中黑色结点为 10、c ,所以从结点 15 到结点 c 的路径上黑色结点数目为 2bh(15)=2

红黑树的黑高则为其根结点的黑高。根据红黑树的性质 3、4,一颗红黑树的黑高 bh >= h/2

Number of nodes from a node to its farthest descendant leaf is no more than twice as the number of nodes to the nearest descendant leaf.

从一个结点到其最远的后代叶结点的顶点数目不会超过从该结点到其最近的叶结点的结点数目的两倍。

这句话的意思就是公式 bh >= h/2,其中黑高 bh 就表示从根结点 6 到离它最近的叶结点 2  包含的结点数 2 ,而 h 则表示从根结点 6 到其最远的叶结点 9  所包含的结点数目 4 ,显然这一公式是合理的。

图解:什么是红黑树?(上篇)

图解:什么是红黑树?(上篇)

引理:一棵有n个内部结点的红黑树的高度 h <= 2lg(n+1)。

关于算法导论中的证明,就不再这里卖关子了,我们用一种更直观的方式来看一下这个问题。

首先我们将下图中的所有红色结点合并到其黑色父结点

图解:什么是红黑树?(上篇)

合并动画演示:

图解:什么是红黑树?(上篇)合并后的结果:

图解:什么是红黑树?(上篇)

也就是刚才的合并产生了一颗 2-3-4 树,这棵树中的每一个结点有2、3 或者 4 个孩子结点。而一颗 2-3-4 树的叶结点有着相同的深度 h'


图解:什么是红黑树?(上篇)

由于从根结点到任何一个叶结点的路径上至多只有高度 h 一半的红色结点数目,所以可以得到 h' >= h/2 结论。

对于一颗有 n+1 个叶子结点的2-3-4 树而言,可以依次得到如下结论:

  1.  .

所以对于一颗有 个结点的红黑树而言,不论查找、删除、查找和最大值、最小值等等的时间复杂度都是 .

红黑树有什么应用呢?

  1. 大多数自平衡BST(self-balancing BST) 库函数都是用红黑树实现的,比如C++中的map 和 set (或者 Java 中的 TreeSet 和 TreeMap)。

  2. 红黑树也用于实现 Linux 操作系统的 CPU 调度。完全公平调度(Completely Fair Scheduler)使用的就是红黑树。

下图来自于为英文的维基百科(该图为Linux的内核图)。

图解:什么是红黑树?(上篇)

图解:什么是红黑树?(上篇)

图解:什么是红黑树?(上篇)

图解:什么是红黑树?(上篇)