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;
    }
     
  • 相关阅读:
    hdu 1002 A + B Problem II
    hdu 1001 Sum Problem
    hdu 1000 A + B Problem
    mysql允许其他电脑访问权限开通
    zend studio安装svn插件
    phpcms采集地址中为相对路径解决方法
    如何解决phpcms后台验证码不显示的问题
    phpcms常用方法简介
    网站标签栏ico设置代码
    phpcms get标签用法
  • 原文地址:https://www.cnblogs.com/zouhong/p/13605122.html
Copyright © 2011-2022 走看看