平衡二叉树之RB树
RB树(红黑树)并不追求“完全平衡”——它只要求部分地达到平衡要求,降低了对旋转的要求,从而提高了性能。由于它的设计,任何不平衡都会在三次旋转之内解决。典型的用途是实现关联数组(如C++中的map和set)
红黑树满足以下特性:
1)每个结点要么是红的,要么是黑的。
2)根结点是黑的。
3)每个叶结点,即空结点是黑的。
4)如果一个结点是红的,那么它的俩个儿子都是黑的。
5)对每个结点,从该结点到其子孙结点的所有路径上包含相同数目的黑结点。
红黑树的优点:可以在O(logn)时间内做查找,插入和删除,因为树高O(logn),具体地,红黑树的高度不会超过2log(n+1)
证明:h <= 2log(n+1) => n >= 2^(h/2) - 1,所以问题转化为证明高度为h的红黑树节点至少有2^(h/2) - 1个
定义从根到叶子节点的黑节点个数为黑高度bh,由红黑树的特性,高度为h的红黑树,黑高度bh >= h/2,所以问题转化为证明n >= 2^bh - 1个
根据数学归纳法
1. 当bh = 0, n = 0;
2. 当bh = 1, n = 1;
3. 假设当bh = h-1时,n = 2^(h-1) - 1
4. 当bh = h时,由3我们可以知道左子树节点个数 = 2^(h-1) - 1, 右子树节点个数 = 2^(h-1) - 1,所以n = 2^(h-1) - 1 + 2^(h-1) - 1 + 1 = 2^h -1,得证。
RB树的插入
只要插入的不是根节点,新插入的节点为红色(这样不会违背性质5),那么只可能违背的是性质4。
情况1:插入的结点的父结点是黑色。
此时不会违反性质4,什么也不用做。这也是2log(n+1)中1的由来。*2存在于,该侧子树原本全黑,另一侧红黑相间
情况2:当前结点的父结点是红色且祖父结点的另一个子结点(叔叔结点)是红色。
此时父结点的父结点一定存在。
对策:将当前节点的父节点和叔叔节点涂黑,祖父结点涂红,把当前结点指向祖父节点,从新的当前节点重新开始算法。
Example:插入
变化后
情况3:当前节点的父节点是红色,叔叔节点是黑色,当前节点是其父节点的右子
对策:当前节点的父节点作为新的当前节点,以新当前节点为支点左旋(本文只讨论左子树情况,由于对称性,右子树情况类似)。(右子树过长,左旋到左子树)
Example:当前节点为7
变化后:
情况4:当前节点的父节点是红色,叔叔节点是黑色,当前节点是其父节点的左子
对策:父节点变为黑色,祖父节点变为红色,在祖父节点为支点右旋(左子树过长,右旋到祖父节点的右子树)
Example: 当前节点为2
变化后
删除操作 (最早的当前节点为待删除的节点)
情况1:当前节点是红色
对策:直接把当前节点染成黑色,结束。
情况2:当前节点是黑色,且兄弟节点为红色(此时父节点和兄弟节点的子节点必须为黑)。
对策:把父节点染成红色,把兄弟结点染成黑色,然后,针对父节点做一次左旋。此变换后原红黑树性质5不变,而把问题转化为兄弟节点为黑色的情况。(左子树变短,左旋)
Example: 当前节点为A
变化后
情况3:当前节点是黑色,且兄弟是黑色,且兄弟节点的两个子节点全为黑色。
解法:把兄弟节点中染红,把父节点当成新的当前节点,重新进入算法。(此变换后性质5不变)
Example: 当前节点为A
变化后
情况4:当前节点颜色是黑色,兄弟节点是黑色,兄弟的左子是红色,右子是黑色。
对策:把兄弟结点染红,兄弟左子节点染黑,之后再在兄弟节点为支点解右旋,之后重新进入算法。此是把当前的情况转化为情况5,而性质5得以保持。
Example:当前节点为A
变化后
情况5:当前节点颜色是黑色,它的兄弟节点是黑色,但是兄弟节点的右子是红色,兄弟节点左子的颜色任意。
对策:把兄弟节点染成当前节点父节点的颜色,把当前节点父节点染成黑色,兄弟节点右子染成黑色,之后以当前节点的父节点为支点进行左旋,此时算法结束,红黑树所有性质调整正确。
Example:当前节点A
变化后