如果我把二叉查找树的每个结点都涂上红色或者黑色。如果它满足下面的5个性质,那么我们就称它为红黑树(Red Black Tree):
- 每个结点不是红色就是黑色。
- 根结点是黑色的。
- 每个叶子结点(NIL)都是黑色的。
- 如果一个结点是红色的,那么它所有的孩子都是黑色的。
- 对于每一个结点,从当前结点到后代的叶子的路径上包含黑色结点的数量是相同的。
根据红黑树的性质,我们可得出下面的结论:具有n内部结点的红黑树的高度最高为2lg(n+1)。
为了方便操作,我们引入一个NIL结点,这个NIL结点是跟普通的结点一样,不过我们只使用color这个域,因为NIL结点是黑色的。
我们定义了结点的颜色,同时每个结点都增加了一个c域来表示结点的颜色。除此之外我们还为树定义了一个nil结点,之前实现的二叉查找树中指向NIL都改为指向T->nil。
1.2.2 查找
因为红黑树本身就是一棵二叉查找树,所以对红黑树的查找操作跟普通的二叉查找树的操作没有什么不一样,但注意的时,在这里我们已经用T.nil取代了0。下面仅仅给出查找操作的代码,读者可以比较一下与普通的二叉查找树有什么不一样的地方。
当插入结点z时,我如果把z着成红色,看看我们会破坏原有的那些性质呢?
我们已经将z着成红色了,所以性质1不会被破坏。当往空的红黑树插入结点的时候,这个性质2会被破坏。每个叶子结点(NIL)都是黑色的,所以性质3不会被破坏。因为我们插入的结点是红色的,所以性质4可能被破坏,同样是因为我们插入的是红色结点,所以性质5不会被破坏。综述,我们往将z着成红色插入到红黑树的时候,可能会破坏性质2和性质4,其它性质不会被破坏。
图9 红黑树的插入情形。(a)z的叔叔是红色的,z是左孩子;(b)z的叔叔是红色的,z是右孩子;(c)z的叔叔是黑色的,z是左孩子;(d)z的叔叔是黑色的,z是右孩子。这里我们略了z的叔叔是左孩子的情况,因为它们是对称的。
当结点z插入时,如果z的双亲结点不是红色的,那么它不会破坏红黑树的性质。所以只有z的双亲是红色的时候,z和z的双亲都是红色会破坏红黑树的性质4,所以我们目的是在不破坏现有的性质1,3,5来恢复性质2和4。
我们考查z和z的叔叔,当z的双亲是左孩子,性质4被破坏的情况只有图9所示的4种情况;当z的双亲是右孩子的时候,跟这个是对称的。我们知道,是因为性质4被破坏了,所以z和z的双亲都红色的,所以z双亲肯定不是根结点,因为根结点是黑色的。下面针对这四种情况进行恢复红黑色树的性质。
情形1:z的叔叔是红色的。
这种情况,不管z是右孩子还是左孩子,我们将z的双亲的双亲上的黑色的分别涂给z的双亲和z的叔叔,这样不会可能破坏性质2或者性质4,其它性质依然保持,因为z的双亲是红色且z的叔叔也是红色的(假设的条件)。我们不知道z的祖亲的双亲颜色,所以我把z的祖亲当作新的结点z,继续调整。
参考资料