zoukankan      html  css  js  c++  java
  • JDK8:HashMap源码解析:TreeNode类的balanceInsertion方法

    一、概述

    balanceInsertion指的是红黑树的插入平衡算法,当树结构中新插入了一个节点后,要对树进行重新的结构化,以保证该树始终维持红黑树的特性。

    关于红黑树的特性:

     性质1. 节点是红色或黑色。

    性质2. 根节点是黑色。

    性质3 每个叶节点(NIL节点,空节点)是黑色的。

    性质4 每个红色节点的两个子节点都是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色节点)

    性质5. 从任一节点到其每个叶子的路径上包含的黑色节点数量都相同。

    二、方法解析

    /**
     * 红黑树插入节点后,需要重新平衡
     * root 当前根节点
     * x 新插入的节点
     * 返回重新平衡后的根节点
     */
    static <K,V> TreeNode<K,V> balanceInsertion(TreeNode<K,V> root,
                                                        TreeNode<K,V> x) {
        x.red = true; // 新插入的节点标为红色
    
        /*
         * 这一步即定义了变量,又开起了循环,循环没有控制条件,只能从内部跳出
         * xp:当前节点的父节点、xpp:爷爷节点、xppl:左叔叔节点、xppr:右叔叔节点
         */
        for (TreeNode<K,V> xp, xpp, xppl, xppr;;) { 
    
            // 如果父节点为空、说明当前节点就是根节点,那么把当前节点标为黑色,返回当前节点
            if ((xp = x.parent) == null) { // L1
                x.red = false;
                return x;
            }
    
            // 父节点不为空
            // 如果父节点为黑色 或者 【(父节点为红色 但是 爷爷节点为空) -> 这种情况何时出现?】
            else if (!xp.red || (xpp = xp.parent) == null) // L2
                return root;
            if (xp == (xppl = xpp.left)) { // 如果父节点是爷爷节点的左孩子  // L3
                if ((xppr = xpp.right) != null && xppr.red) { // 如果右叔叔不为空 并且 为红色  // L3_1
                    xppr.red = false; // 右叔叔置为黑色
                    xp.red = false; // 父节点置为黑色
                    xpp.red = true; // 爷爷节点置为红色
                    x = xpp; // 运行到这里之后,就又会进行下一轮的循环了,将爷爷节点当做处理的起始节点 
                }
                else { // 如果右叔叔为空 或者 为黑色 // L3_2
                    if (x == xp.right) { // 如果当前节点是父节点的右孩子 // L3_2_1
                        root = rotateLeft(root, x = xp); // 父节点左旋,见下文左旋方法解析
                        xpp = (xp = x.parent) == null ? null : xp.parent; // 获取爷爷节点
                    }
                    if (xp != null) { // 如果父节点不为空 // L3_2_2
                        xp.red = false; // 父节点 置为黑色
                        if (xpp != null) { // 爷爷节点不为空
                            xpp.red = true; // 爷爷节点置为 红色
                            root = rotateRight(root, xpp);  //爷爷节点右旋,见下文右旋方法解析
                        }
                    }
                }
            }
            else { // 如果父节点是爷爷节点的右孩子 // L4
                if (xppl != null && xppl.red) { // 如果左叔叔是红色 // L4_1
                    xppl.red = false; // 左叔叔置为 黑色
                    xp.red = false; // 父节点置为黑色
                    xpp.red = true; // 爷爷置为红色
                    x = xpp; // 运行到这里之后,就又会进行下一轮的循环了,将爷爷节点当做处理的起始节点 
                }
                else { // 如果左叔叔为空或者是黑色 // L4_2
                    if (x == xp.left) { // 如果当前节点是个左孩子 // L4_2_1
                        root = rotateRight(root, x = xp); // 针对父节点做右旋,见下文右旋方法解析
                        xpp = (xp = x.parent) == null ? null : xp.parent; // 获取爷爷节点
                    }
                    if (xp != null) { // 如果父节点不为空 // L4_2_4
                        xp.red = false; // 父节点置为黑色
                        if (xpp != null) { //如果爷爷节点不为空
                            xpp.red = true; // 爷爷节点置为红色
                            root = rotateLeft(root, xpp); // 针对爷爷节点做左旋
                        }
                    }
                }
            }
        }
    }
    
    
    /**
     * 节点左旋
     * root 根节点
     * p 要左旋的节点
     */
    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) { // 要左旋的节点以及要左旋的节点的右孩子不为空
            if ((rl = p.right = r.left) != null) // 要左旋的节点的右孩子的左节点 赋给 要左旋的节点的右孩子 节点为:rl
                rl.parent = p; // 设置rl和要左旋的节点的父子关系【之前只是爹认了孩子,孩子还没有答应,这一步孩子也认了爹】
    
            // 将要左旋的节点的右孩子的父节点  指向 要左旋的节点的父节点,相当于右孩子提升了一层,
            // 此时如果父节点为空, 说明r 已经是顶层节点了,应该作为root 并且标为黑色
            if ((pp = r.parent = p.parent) == null) 
                (root = r).red = false;
            else if (pp.left == p) // 如果父节点不为空 并且 要左旋的节点是个左孩子
                pp.left = r; // 设置r和父节点的父子关系【之前只是孩子认了爹,爹还没有答应,这一步爹也认了孩子】
            else // 要左旋的节点是个右孩子
                pp.right = r; 
            r.left = p; // 要左旋的节点  作为 他的右孩子的左节点
            p.parent = r; // 要左旋的节点的右孩子  作为  他的父节点
        }
        return root; // 返回根节点
    }
    
    /**
     * 节点右旋
     * root 根节点
     * p 要右旋的节点
     */
    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) { // 要右旋的节点不为空以及要右旋的节点的左孩子不为空
            if ((lr = p.left = l.right) != null) // 要右旋的节点的左孩子的右节点 赋给 要右旋节点的左孩子 节点为:lr
                lr.parent = p; // 设置lr和要右旋的节点的父子关系【之前只是爹认了孩子,孩子还没有答应,这一步孩子也认了爹】
    
            // 将要右旋的节点的左孩子的父节点  指向 要右旋的节点的父节点,相当于左孩子提升了一层,
            // 此时如果父节点为空, 说明l 已经是顶层节点了,应该作为root 并且标为黑色
            if ((pp = l.parent = p.parent) == null) 
                (root = l).red = false;
            else if (pp.right == p) // 如果父节点不为空 并且 要右旋的节点是个右孩子
                pp.right = l; // 设置l和父节点的父子关系【之前只是孩子认了爹,爹还没有答应,这一步爹也认了孩子】
            else // 要右旋的节点是个左孩子
                pp.left = l; // 同上
            l.right = p; // 要右旋的节点 作为 他左孩子的右节点
            p.parent = l; // 要右旋的节点的父节点 指向 他的左孩子
        }
        return root;
    }
     
  • 相关阅读:
    手机号码正则表达式
    POJ 3233 Matrix Power Series 矩阵快速幂
    UVA 11468
    UVA 1449
    HDU 2896 病毒侵袭 AC自动机
    HDU 3065 病毒侵袭持续中 AC自动机
    HDU 2222 Keywords Search AC自动机
    POJ 3461 Oulipo KMP模板题
    POJ 1226 Substrings KMP
    UVA 1455 Kingdom 线段树+并查集
  • 原文地址:https://www.cnblogs.com/zouhong/p/13605122.html
Copyright © 2011-2022 走看看