红黑树只有以下 5 条性质(很简单,很好记,最好按顺序记忆)
- 树中结点只有两种颜色,红色和黑色
- 树的根是黑色结点
- 每个叶结点(
nil[T]
也叫哨兵结点)都是黑色结点 - 红色结点的子红点必须为黑色结点
- 对任意结点,从它到它子孙叶结点的所有路径上包含相同数目的黑色结点
红黑树特点:查找效率高,即使在最坏情况下也有很好的性能,因为红黑树最长路径(红黑相间)高度不会比最短路径(全是黑结点)多出一倍。红黑树是一种特殊的二叉查找树,理解红黑树的构建、插入、删除操作,需要先明白以下概念
- 二叉查找树
- 中序遍历(LDR 遍历)
- 前趋、后继
- 左旋、右旋
本文从二叉查找树说起,涉及上面说的 4 点,最终会构建红黑树,并完成对红黑树的插入与删除操作。
二叉查找树
为了方面叙说,作以下说明:
如果 k 为一个结点,那么 key[k]
为结点 k 的值,left[k]
为结点 k 的左孩子结点,right[k]
为结点 k 的右孩子结点,p[k]
为结点 k 的父结点。
root(T)
为二叉树的根结点
二叉查找树是一棵有序的树,对于树中的结点 k 来说,它的任意左子孙结点 l 都有如下规则: key[l] <= key[k]
;它的任意右子孙结点 r 都服从 key[k] <= key[r]
规则。比如下面两个都是二叉查找树
插入
现在使用这两个树展示插入操作,插入值为 6:
图中每条蓝色的线代表一次比较操作,很明显向 B 中插入一个数所需的步骤比较多,这种情况是由于树太‘偏’造成的,如果所有的树都像 A 这样‘正’就完美了。
删除
删除结点有三种情况
情况一:待删除结点 k 是叶子结点,比如 A 中的 2 5 6 8 结点,B 中的 5 6 8 结点,对于这些结点直接删除就可以了,删除后不会破坏二叉查找树特性,因为除了被删除的结点,其它结点没有任何变化
情况二:待删除结点 k 有一个孩子,如 B 中的 2 3,对于这些结点删除后用它的子结点直接放到删除结点的位置
情况三:待删除结点 k 有两个孩子,如 A 中的 5 3 7 结点,B 中的 7 5 结点,此时将 k 删除,取出 k 中序遍历的后继放到 k 的位置,就完成了删除处理
下图解释了什么是中序遍历,什么是前趋和后继
不知道是谁把 LDR 遍历翻译成了中序遍历,反正我总是把它与前序遍历后序遍历搞混。感觉直接记 LDR、LRD、DLR 会比较知道是什么顺序。
OK,知道如何找 LDR 顺序的后继了,下面用图表示,如果删除有两个孩子的结点。最简单的一种情况如下(红色代表待删除结点,绿色代表后继,有红绿色盲的姥爷们对不住了。。。)
上面两种操作本质是一样的,待删除结点的后继都没有孩子结点,这种情况,直接将后继替换掉红色结点就完成删除操作了。但是另外一种情况需要多处理一步,比如下图:
此时绿色节点有个右孩子(绿色结点为红色结点 LDR 序的后继,所以它不可能有左孩子),需要先对灰色部分处理,处理方式如 情况二 先对绿色结点进行删除,然