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树。

 

posted @ 2019-03-08 17:14  leondryu  阅读(419)  评论(0)    收藏  举报