红黑树满足如下性质:
1、每个结点要么是红的要么是黑的。
2、根结点是黑的。
3、每个叶结点(叶结点即指树尾端NIL指针或NULL结点)都是黑的。
4、如果一个结点是红的,那么它的两个儿子都是黑的。
5、 对于任意结点而言,其到叶结点树尾端NIL指针的每条路径都包含相同数目的黑结点。
这些约束确保了红黑树的关键特性:从根到叶子的最长的可能路径不多于最短的可能路径的两倍长。结果是这个树大致上是平衡的。因为操作比如插入、删除和查找某个值的最坏情况时间都要求与树的高度成比例,这个在高度上的理论上限允许红黑树在最坏情况下都是高效的,而不同于普通的二叉查找树。
要知道为什么这些性质确保了这个结果,注意到性质4导致了路径不能有两个毗连的红色节点就足够了。最短的可能路径都是黑色节点,最长的可能路径有交替的红色和黑色节点。因为根据性质5所有最长的路径都有相同数目的黑色节点,这就表明了没有路径能多于任何其他路径的两倍长。
在很多树数据结构的表示中,一个节点有可能只有一个子节点,而叶子节点包含数据。用这种范例表示红黑树是可能的,但是这会改变一些性质并使算法复杂。为此,本文中我们使用”nil叶子”或”空(null)叶子”,如上图所示,它不包含数据而只充当树在此结束的指示。这些节点在绘图中经常被省略,导致了这些树好像同上述原则相矛盾,而实际上不是这样。与此有关的结论是所有节点都有两个子节点,尽管其中的一个或两个可能是空叶子。
引理:
一颗有 n 个内结点的红黑树的高度至多为 2lg(n+1)。
旋转:旋转的目的是让树保持红黑树的特性。
左旋:被旋转的结点将变成一个左孩子节点
右孩子成为父节点,并且右孩子的原左孩子成为被旋转结点的右孩子。
右旋:被旋转的结点将变成一个右节点
插入和修复
插入的结点是红色的,并将其孩子指针指向NIL结点。(如果插入黑色结点是不会影响任何规则的,所以就不能调整了)。主要需要调整的是插入结点的父节点是红色的情况。
1、如果插入的是根结点,由于原树是空树,此情况只会违反性质2,因此直接把此结点涂为黑色
2、如果插入的结点的父结点是黑色,由于此不会违反性质2和性质4,红黑树没有被破坏,所以此时什么也不做。
3、 插入修复情况1:如果当前结点的父结点是红色且祖父结点的另一个子结点(叔叔结点)是红色:
分析:
此时父结点的父结点一定存在,否则插入前就已不是红黑树。与此同时,又分为父结点是祖父结点的左孩子还是右孩子,根据对称性,我们只要解开一个方向就可以了。这里只考虑父结点为祖父左孩子的情况,如下图所示。
算法:
对此,我们的解决策略是:将当前节点的父节点和叔叔节点涂黑,祖父结点涂红,把当前结点指向祖父节点,从新的当前节点重新开始算法,于是,插入修复情况1转换成了插入修复情况2。
4、插入修复情况2:当前节点的父节点是红色,叔叔节点是黑色,当前节点是其父节点的右子
解决对策是:当前节点的父节点做为新的当前节点,以新当前节点为支点左旋。变成了情况3
5、插入修复情况3:当前节点的父节点是红色,叔叔节点是黑色,当前节点是其父节点的左孩子
解决对策是:父节点变为黑色,祖父节点变为红色,在祖父节点为支点右旋。
变成成最终结果