紅黑樹基礎-第二篇

本文主要解析java8中HashMap的紅黑樹源碼部分,如有不懂可以先看我寫的紅黑樹基礎-第一篇

1.左旋

注意:圖中節點標號跟下面代碼中變量名一致,便於理解。

/**
 * 紅黑樹節點左旋操作
 * @param root 根節點
 * @param p 旋轉節點
 * @return 新root節點
 */
static <K,V> TreeNode<K,V> rotateLeft(TreeNode<K,V> root,
                                      TreeNode<K,V> p) {
    TreeNode<K,V> r, pp, rl;
    if (p != null && (r = p.right) != null) {
        //1.把r的左子樹變成p的右子樹,並標記爲rl,即p->right=rl
        if ((rl = p.right = r.left) != null)
            //2.如果rl不爲空,鏈起來,rl->parent=p
            rl.parent = p;
        //3.把p的父節點變成r的父節點,並標記爲pp,即r->parent=pp
        if ((pp = r.parent = p.parent) == null)
            //4.如果pp爲空,那麼之前p是根節點,現在左旋後r變成根節點,root=r,並且根節點爲黑色,r.red=false
            (root = r).red = false;
        //5.否則pp不爲空,就判斷p是否是pp的左孩子,如果是,旋轉後r就是左孩子
        else if (pp.left == p)
            pp.left = r;
        else
            //6.否則就是右孩子
            pp.right = r;
        //7.最後完善樹的父子關係,鏈起來
        //r->left=p
        r.left = p;
        //p->parent=r
        p.parent = r;
    }
    //最後返回新的root節點
    return root;
}

2.右旋

/**
 * 紅黑樹節點右旋操作
 * @param root 根節點
 * @param p 旋轉節點
 * @return 新root節點
 */
static <K,V> TreeNode<K,V> rotateRight(TreeNode<K,V> root,
                                       TreeNode<K,V> p) {
    TreeNode<K,V> l, pp, lr;
    if (p != null && (l = p.left) != null) {
        //1.把l的右子樹變成p的左子樹,並標記爲lr,即p->left=lr
        if ((lr = p.left = l.right) != null)
            //2.如果lr不爲空,鏈起來,lr->parent=p
            lr.parent = p;
        //3.把p的父節點變成l的父節點,並標記爲pp,即l->parent=pp
        if ((pp = l.parent = p.parent) == null)
            //4.如果pp爲空,那麼之前p是根節點,現在右旋後l變成根節點,root=l,並且根節點爲黑色,l.red=false
            (root = l).red = false;
        //5.否則pp不爲空,就判斷p是否是pp的右孩子,如果是,旋轉後l就是右孩子
        else if (pp.right == p)
            pp.right = l;
        else
            //6.否則就是左孩子
            pp.left = l;
        //7.最後完善樹的父子關係,鏈起來
        //l->right=p
        l.right = p;
        //p->parent=l
        p.parent = l;
    }
    //最後返回新的root節點
    return root;
}

3.平衡插入

講解平衡插入之前,我們直接從源碼入手,如果感覺理解起來有困難,請先看懂我寫的紅黑樹基礎-第一篇

下面約定下面代碼變量的命名規則,x爲邏輯上待插入的節點(有可能是第一次插入,也有可能是自底向上調整時的插入),xp爲x節點的父節點,xpp爲x節點的祖父節點,也是xp的父節點。xppl是xpp的左孩子,xppr是xpp的右孩子。xp是xppl和xppr中的其中之一。

/**
 * 紅黑樹節點平衡插入操作
 * @param root 根節點
 * @param x 插入節點
 * @return 新root節點
 */
static <K,V> TreeNode<K,V> balanceInsertion(TreeNode<K,V> root,
                                            TreeNode<K,V> x) {
    //首先插入節點x的顏色初始化爲紅色
    x.red = true;
    //for循環沒有結束條件,自底向上插入循環處理,直至到root或者紅黑自平衡退出循環
    for (TreeNode<K,V> xp, xpp, xppl, xppr;;) {
        //如果xp爲空,說明x已經爲root了
        if ((xp = x.parent) == null) {
            x.red = false;
            return x;
        }
        //否則判斷父節點xp是否是黑色,或者xp是否爲root
        else if (!xp.red || (xpp = xp.parent) == null)
            return root;
        //判斷父節點xp是否是祖父節點的左孩子
        if (xp == (xppl = xpp.left)) {
            //叔叔節點存在,並且爲紅節點
            if ((xppr = xpp.right) != null && xppr.red) {
                //紅黑樹基礎-第一篇,根據情景4.1進行變色
                xppr.red = false;
                xp.red = false;
                xpp.red = true;
                //以xpp爲插入節點,進行下一次循環
                x = xpp;
            }
            //否則叔叔節點要麼爲空,要麼爲黑色
            else {
                //如果插入節點x是父節點xp的右孩子,那麼滿足紅黑樹基礎-第一篇,情景4.2.2
                if (x == xp.right) {
                    //把xp進行左旋,同時把插入節點x賦值爲xp
                    root = rotateLeft(root, x = xp);
                    //重新設置xp和xpp
                    xpp = (xp = x.parent) == null ? null : xp.parent;
                }
                //進行紅黑樹基礎-第一篇,情景4.2.1的處理
                if (xp != null) {
                    xp.red = false;
                    if (xpp != null) {
                        xpp.red = true;
                        //xpp右旋
                        root = rotateRight(root, xpp);
                    }
                }
            }
        }
        //否則父節點xp是祖父節點的右孩子
        else {
            //叔叔節點存在,並且爲紅節點
            if (xppl != null && xppl.red) {
                //紅黑樹基礎-第一篇,根據情景4.1進行變色
                xppl.red = false;
                xp.red = false;
                xpp.red = true;
                //以xpp爲插入節點,進行下一次循環
                x = xpp;
            }
            //否則叔叔節點要麼爲空,要麼爲黑色
            else {
                //如果插入節點x是父節點xp的左孩子,那麼滿足紅黑樹基礎-第一篇,情景4.3.2
                if (x == xp.left) {
                    //把xp進行右旋,同時把插入節點x賦值爲xp
                    root = rotateRight(root, x = xp);
                    //重新設置xp和xpp
                    xpp = (xp = x.parent) == null ? null : xp.parent;
                }
                //進行紅黑樹基礎-第一篇,情景4.3.1的處理
                if (xp != null) {
                    xp.red = false;
                    if (xpp != null) {
                        xpp.red = true;
                        //xpp左旋
                        root = rotateLeft(root, xpp);
                    }
                }
            }
        }
    }
}

4.平衡刪除

未完待續

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章