HashMap 中 balanceInsertion()方法详解

766 阅读4分钟

1.前言

这个方法的作用是:当红黑树中新插入一个节点时,需要对树的结构重新优化,以保证该树始终保持红黑树的特性。

红黑树的特性:

  1. 每个结点是黑色或者红色。
  2. 根结点是黑色。
  3. 每个叶子结点(NIL)是黑色。 [注意:这里叶子结点,是指为空(NIL或NULL)的叶子结点!]
  4. 如果一个结点是红色的,则它的子结点必须是黑色的。
  5. 每个结点到叶子结点NIL所经过的黑色结点的个数一样的。[确保没有一条路径会比其他路径长出俩倍,所以红黑树是相对接近平衡的二叉树的!]

2.源码

static <K,V> TreeNode<K,V> balanceInsertion(TreeNode<K,V> root,
                                            TreeNode<K,V> x) {
    //1. x:插入的节点,root:红黑树的根节点,先把x的颜色设为红色
    x.red = true;
    
    // 这里定义了一个循环,没有结束条件,即只能从内部退出
    // xp:x节点的父节点,xpp:x节点的父父节点,xppl:x节点的父父节点的左子节点,xppr:x节点的父父节点的右子节点
    for (TreeNode<K,V> xp, xpp, xppl, xppr;;) {
        //2. 这里判断 把x的父节点先赋给 xp,当xp为null 时,则说明x就是root节点,无需进行平衡
        if ((xp = x.parent) == null) {
            // 把x的颜色设为黑色
            x.red = false;
            // 直接返回 x
            return x;
        }
        //3. 当 xp的颜色是黑色的,或者 【xp的颜色是红色且它的parent是null,这种情况不会出现,这样写通常是为了赋值】
        else if (!xp.red || (xpp = xp.parent) == null)
            return root;
         // 4. 如果当前的父节点节点 == 当前节点的父父节点的左子节点 
        if (xp == (xppl = xpp.left)) {
            // 4.1 把xpp.right赋给 xppr 当 xppr不为null且 xppr是红色的
            if ((xppr = xpp.right) != null && xppr.red) {
                // 把xppr修改为黑色
                xppr.red = false;
                // xp 修改为黑色的
                xp.red = false;
                // xpp修改为红色
                xpp.red = true;
                // 把 xpp 赋给 x
                x = xpp;
            }
            else {
                // 4.2.1 当 x== x父节点的右子节点时,即x被放到了右边
                if (x == xp.right) {
                    // 父节点左旋  2.1 rotateLeft 详解
                    root = rotateLeft(root, x = xp);
                    xpp = (xp = x.parent) == null ? null : xp.parent;
                }
                // 4.2.2 当xp 不为 null时,把xp的颜色置为黑色
                if (xp != null) {
                    xp.red = false;
                    // 4.2.2.1 当 xpp 不为null时,把 xpp的颜色置为红色
                    if (xpp != null) {
                        xpp.red = true;
                        // xpp 节点右旋, 2.2 rotateRight 详解
                        root = rotateRight(root, xpp);
                    }
                }
            }
        }
        // 5.
        else {
            // 5.1 当 xppl 不为null 且 xppl的颜色是红色时
            if (xppl != null && xppl.red) {
                // 把 xppl的颜色置为黑色
                xppl.red = false;
                // xp的颜色置为黑色
                xp.red = false;
                // xpp 的颜色设为红色
                xpp.red = true;
                // 把 xpp赋给 x
                x = xpp;
            }
            // 5.2
            else {
                // 5.2.1 当x== xp的左子节点时,即x在它父节点的左边
                if (x == xp.left) {
                    // 父节点右旋 2.2 rotateRight 详解
                    root = rotateRight(root, x = xp);
                    xpp = (xp = x.parent) == null ? null : xp.parent;
                }
                // 5.2.2 当xp不为null 时
                if (xp != null) {
                    // 把xp的颜色设为 黑色
                    xp.red = false;
                    // 如果 xpp 不为null
                    if (xpp != null) {
                        // 把xpp的颜色设为红色
                        xpp.red = true;
                        // xpp 左旋 2.1 rotateLeft 详解
                        root = rotateLeft(root, xpp);
                    }
                }
            }
        }
    }
}

2.1 rotateLeft()

// 节点左旋
// root 根节点,p要左旋的节点
static <K,V> TreeNode<K,V> rotateLeft(TreeNode<K,V> root,
                                      TreeNode<K,V> p) {
    // r:p的右子节点,pp:p的的父节点 rl :r的左子节点
    TreeNode<K,V> r, pp, rl;
    // 当 p不等于 null && p.right(此时已把p.right赋给r) 不为null
    // 即需要左旋时,p和p的右子节点不能为null
    if (p != null && (r = p.right) != null) {
        // 这里实际上是 先把 r.left赋给 p.right 然后再把 p.right 赋给 rl 即 r的左子节点
        // 当 rl 不为null
        if ((rl = p.right = r.left) != null)
            // 把rl的parent 设为 p;
            rl.parent = p;
         // 把p.parent 赋给 r.parent 最后赋给 pp
        // 当 p的父节点不为null时,即r就是root节点
        if ((pp = r.parent = p.parent) == null)
            // 把r赋给 root 把它的颜色设为 黑色
            (root = r).red = false;
        // 当 pp.left == p 即说明p是它父节点的左子节点
        else if (pp.left == p)
            // 这里把它父节点的左子节点 改为 r
            pp.left = r;
        else
            // 否则的话 说明p是它父节点的右子节点
            pp.right = r;
        // 把r.的左子节点设为 p
        r.left = p;
        // p的父节点设为 r;
        p.parent = r;
    }
    return root;
}

2.2 rotateRight()

// 节点右旋
// root 根节点,p要右旋的节点
static <K,V> TreeNode<K,V> rotateRight(TreeNode<K,V> root,
                                       TreeNode<K,V> p) {
    // l:p的左子节点, pp:p的父节点,lr:l的右子节点
    TreeNode<K,V> l, pp, lr;
    // 当p不为null且P的左子节点不能为null(节点右旋的必要条件)
    if (p != null && (l = p.left) != null) {
        // l的右子节点 不为null时
        if ((lr = p.left = l.right) != null)
            // 因为上面已经把l的右子节点赋给了 p的左子节点,所以这里在把 lr的父节点设为 p
            lr.parent = p;
        // 判断当 pp 为null时
        if ((pp = l.parent = p.parent) == null)
            // 把l赋给root 并把它的颜色设为黑色
            (root = l).red = false;
        // 当 pp 的右子节点 就是p时,直接把pp的右子节点设为l
        else if (pp.right == p)
            pp.right = l;
        // 否则把 pp的左子节点赋给 l
        else
            pp.left = l;
        // l的右子节点设为p
        l.right = p;
        // p的父节点设为 l
        p.parent = l;
    }
    //返回 root节点
    return root;
}