LLRB 左倾红黑树(二)deleteMax疑问
deleteMax
deleteMax自顶向下的过程中,要保证当前节点或者其右孩子是红色
当前节点红色——当前节点是3-节点的右端节点
当前节点的右孩子是红色——当前节点的右孩子是一个3-节点
前提,这里的代码是针对最终形态2-3红黑树(即左倾红黑树),而不是最终形态234红黑树。(代码来源:http://www.cs.princeton.edu/~rs/talks/LLRB/Java/RedBlackBST.java)
我强调最终形态,是因为,在变换过程中会产生4-节点甚至5-节点,4-节点不处理,5-节点分解。但是最终形态必然是2-3的形态。
步骤1:若当前节点h左孩子是红色,则右旋转
步骤2:若当前节点h右孩子是null,则当前节点h是max,return null
步骤3:若当前节点h不是max,且h的右孩子黑色,h的右孩子的左孩子黑色,执行moveRedRight(这里隐含条件,h的左孩子是黑色,因为若h的左孩子是红色,则步骤1会右旋转,h的右孩子就会变成红色,与当前if条件不符)
步骤4:当前节点存在右孩子,对其执行deleteMax
步骤5:从底层一路向上,fixUp修复树的颜色。
其中,从步骤1到步骤3,都是从自顶向下,步骤4处理deleteMax,步骤5自底向上修复。
private Node deleteMax(Node h)
{
if (isRed(h.left))
h = rotateRight(h);
if (h.right == null)
return null;
if (!isRed(h.right) && !isRed(h.right.left))
h = moveRedRight(h);
h.right = deleteMax(h.right);
return fixUp(h);
}
moveRedRight函数,这是最让我不解的地方,
首先,进入moveRedRight的条件是,h左右孩子,h右孩子的左孩子,都黑色;h本身的颜色不讨论
步骤1:h变色,变色后,h左右孩子都红色
步骤2:判断h左孩子的左孩子是否红色,若为红色,此时h的左侧有两条连续的红链
对h执行右旋转,
然后变色
最后return h;
因为deleteMax的目的,就是让当前节点h,或者其右孩子h.right,其中一个保证为红色。那么当进入到moveRedRight之后,第一步colorFlip已经把h.right变成红色了,我认为没有必要再执行if(isRed(h.left.left))中的代码。
private Node moveRedRight(Node h)
{ // Assuming that h is red and both h.right and h.right.left
// are black, make h.right or one of its children red.
colorFlip(h);
if (isRed(h.left.left)) //我认为删掉if块里面的语句完全没问题。
{
h = rotateRight(h);
colorFlip(h);
}
return h;
}
删掉 if( isRed(h.left.left)) 及其内容,不会影响2-3红黑树的平衡。为了验证我的想法,作以下实验
测试代码:http://www.cs.princeton.edu/~rs/talks/LLRB/Java/RedBlackBST.java,将moveRedRight()改为如下内容,
private Node moveRedRight(Node h)
{
colorFlip(h);
return h;
}
首先我需要找一种符合以上情况的情形,h.left.left是红色,且h.left,h.right是黑色。
取字符数组,A H E F L K I C J G B D,按照这个顺序,插入到RedBlackBst中,
RedBlackBST2<String, Integer> st = new RedBlackBST2<String, Integer>(2);
String[] a = "AHEFLKICJGBD".split("");
for (int i = 0; i < a.length; i++)
st.put(a[i], i);
插入后,红黑树图如下

此红黑树,对应的2-3树图

当调用deleteMax之后



可以看出,删掉if(isRed(h.left.left))及其内部代码之后,虽然结果红黑树的分布情况会有变化,但并不会影响最终2-3红黑树的平衡,没有出现右倾红链,也没有出现连续红链。
可能会有人说,你这个是特例。针对这个疑问,可以做一个简单验证,还是将源代码的deleteMax中if条件及if内部内容全部去掉,用以下代码先生成一个随机的23红黑树,再通过deleteMax将树删除到只剩一个节点,每删除一次,调用st.check检查红黑树是否平衡,若不平衡则打印check不通过。
我运行了很多次,从来不会出现check不通过的情况。也就是说moveRedRight中的 if(isRed(h.left.left))并不会影响树的平衡,那为什么非要有这个操作?作者在书里、pdf课件里,都没有解释清楚。
RedBlackBST2<Integer, Integer> st = new RedBlackBST2<>(2);
for (int i=0;i<arr.length;i++){
st.put(Integer.parseInt(arr[i]),i);
}
for (int i=st.size();i>1;i--){
st.deleteMax();
if (!st.check()){
System.out.println("check不通过");
}
}
我唯一能想到的可能的影响是,当进入moveRedRight,h的左右孩子都变为红色,若h.right.left为红色(h.right.right必然黑色,因为这本身是个23红黑树)而不进行任何操作,这会形成一个5-节点(即4个节点形成5条链,这就不是234树了,而是2345树),如下图。然而,pdf并没有说不允许5-节点出现;况且这种5-节点并不是持久的,因为在自底向上的过程中,fixUp会一层层修复之前的节点,从而保证最后是一颗23树。


浙公网安备 33010602011771号