JDK1.8 源码分析(七) -- HashMap中的红黑树TreeNode

239 阅读22分钟

1. 类的定义

static final class TreeNode extends LinkedHashMap.Entry

从定义中可以看出

  • TreeNode 是一个HashMap的一个静态内部类,且使用final修改,不能被继承

  • TreeNode继承LinkedHashMap.Entry,表明TreeNode是一个键值对的对象

2. 字段属性

        //父节点
        TreeNode parent;  // red-black tree links
        //左子节点
        TreeNode left;
        //右子节点
        TreeNode right;
        //前一个节点
        TreeNode prev;    // needed to unlink next upon deletion
        //红黑标记
        boolean red;
        //hash值,父类继承
        final int hash;
        //key 父类继承
        final K key;
        //value 父类继承
        V value;
        //下一个节点 父类继承
        Node next;

从字段属性中可以看出

  • TreeNode既是红黑树结构也是双向链表结构

3. 构造函数

TreeNode(int hash, K key, V val, Node next) {
            //调用父类的构造函数
            super(hash, key, val, next);
        }

4. 方法

root 方法

 //获取根节点
 final TreeNode root() {
             //从当前节点循环,一直向上找父节点,直到节点的父节点为null,则此节点就是根节点
             //无限循环,把当前节点赋值给r
            for (TreeNode r = this, p;;) {
                //查找r的父节点
                if ((p = r.parent) == null)
                    //如果父节点为空,此节点为根节点
                    return r;
                //把父节点赋值给r,继续循环
                r = p;
            }
        }

moveRootToFront 方法

//把root元素添加到tab对应插槽的第一个元素,此方法针对链表结构进行操作,不对红黑树操作
static <K,V> void moveRootToFront(Node<K,V>[] tab, TreeNode<K,V> root) {
    		//记录tab的长度
            int n;
    		//如果root不为空,tab不为空,tab长度大于0
            if (root != null && tab != null && (n = tab.length) > 0) {
                //通过(n - 1) & root.hash计算root在tab插槽的位置
                int index = (n - 1) & root.hash;
                //获取root的hash值tab中对应插槽的第一个元素
                TreeNode<K,V> first = (TreeNode<K,V>)tab[index];
               	//如果root不是对一个元素
                if (root != first) {
                    //存储root的后一个元素
                    Node<K,V> rn;
                    //把root设置为对应插槽的第一个元素
                    tab[index] = root;
                    //获取root前一个元素
                    TreeNode<K,V> rp = root.prev;
                    //如果root的下一个元素不为空
                    if ((rn = root.next) != null)
                        //把root的后一个元素的前一个元素引用指向root的前一个元素,即跳过root元素
                        ((TreeNode<K,V>)rn).prev = rp;
                    //如果root的前一个元素不为空
                    if (rp != null)
                        //把root的上一个元素的后一个元素引用指向root的后一个元素,即跳过root元素
                        rp.next = rn;
                    //如果插槽中以前的第一个元素不为空
                    if (first != null)
                        //把以前的第一个元素的前一个元素的引用指向root
                        first.prev = root;
                    //root的后一个元素的引用指向以前的第一个元素
                    root.next = first;
                    //root的前一个元素的引用置为null
                    root.prev = null;
                }
                assert checkInvariants(root);
            }
        }

find 方法

//根据hash值和key查找元素
final TreeNode<K,V> find(int h, Object k, Class<?> kc) {
    		//获取当前元素,后面表示当前遍历的元素
            TreeNode<K,V> p = this;
            do {
                //ph 当前元素的hash值
                //dir 小于等于0,往左子节点查找,大于0往右子节点查找
                //pk 当前元素的key
                int ph, dir; K pk;
                //pl 当前元素的左节点
                //pr 当前元素的右节点
                TreeNode<K,V> pl = p.left, pr = p.right, q;
                if ((ph = p.hash) > h)
                    //如果当前元素的hash值大于传入的hash值,当前元素设置为当前元素的左节点
                    p = pl;
                else if (ph < h)
                    //如果当前元素的hash值小于传入的hash值,当前元素设置为当前元素的右节点
                    p = pr;
                else if ((pk = p.key) == k || (k != null && k.equals(pk)))
                    //如果当前元素的key等于传入的key,表示找到,直接返回当前元素即可
                    return p;
                else if (pl == null)
                    //如果当前元素的左节点为空,当前元素设置为当前元素的右节点
                    p = pr;
                else if (pr == null)
                    //如果当前元素的右节点为空,当前元素设置为当前元素的左节点
                    p = pl;
                else if ((kc != null ||
                          (kc = comparableClassFor(k)) != null) &&
                         (dir = compareComparables(kc, k, pk)) != 0)
                    //如果类不相等或者k小于pk当前元素设置为当前元素的左节点,反之设置为右节点
                    p = (dir < 0) ? pl : pr;
                else if ((q = pr.find(h, k, kc)) != null)
                    //递归查找,找到就返回
                    return q;
                else
                    p = pl;
               //继续循环
            } while (p != null);
    		//没找到,返回null
            return null;
        }

getTreeNode 方法

final TreeNode<K,V> getTreeNode(int h, Object k) {
    		//通过调用find方法查找
            return ((parent != null) ? root() : this).find(h, k, null);
        }

removeTreeNode 方法

final void removeTreeNode(HashMap<K,V> map, Node<K,V>[] tab,
                                  boolean movable) {
    		//注意这是当前节点的方法,有个this引用存在,删除的是当前节点
            int n;
    		//如果table为空直接返回
            if (tab == null || (n = tab.length) == 0)
                return;
    		//根据hash值获取对应插槽的位置
            int index = (n - 1) & hash;
    	    //获取插槽第一个节点
    		//first 插槽中第一个节点
    		//root 红黑树的根节点
    		//rl 根节点的左子节点
            TreeNode<K,V> first = (TreeNode<K,V>)tab[index], root = first, rl;
    		//succ 下一个节点的副本
    		//pred 上一个节点的副本
            TreeNode<K,V> succ = (TreeNode<K,V>)next, pred = prev;
    		//这里能体现出红黑树结构内部也有链表结构
            if (pred == null)
                //如果上一个节点为null,说明当前节点是第一个节点
                //因为是删除的是当前节点,所以插槽第一个节点赋值为下一个节点
                tab[index] = first = succ;
            else
                //如果上一个节点不为null,把上一个节点的引用指向下一个节点
                pred.next = succ;
            if (succ != null)
                //如果下一个节点不为null,把下一个节点的上一个节点的引用指向当前节点的上一个节点
                succ.prev = pred;
            if (first == null)
                //如果第一个节点为null,表示插槽没有元素,直接返回
                //这里表示最开始插槽中只有当前元素一个元素,实际上不会有这种可能
                return;
            if (root.parent != null)
                //如果根节点还存在父节点,把根节点置修改为树的根节点
                root = root.root();
            if (root == null || root.right == null ||
                (rl = root.left) == null || rl.left == null) {
                //如果根节点为null 根节点的右子节点为null 根节点的左子节点为null 根节点的左子节点的左子节点为null 
                //这些都表示当前插槽的节点数量太少,将红黑树转换为链表 直接返回
                tab[index] = first.untreeify(map);  // too small
                return;
            }
    		//p 当前节点的副本
    		//pl 当前节点的左子节点
    		//pr 当前节点的右子节点
    		//replacement 需要替换的节点
            TreeNode<K,V> p = this, pl = left, pr = right, replacement;
    		//------------------------------删除一个节点只有四种情况--------------------------------
    		//******************第一种情况 删除的节点集邮左子树又有右子树***********
    		//******************把当前节点和当前节点的右子节点的最左子节点对换***********
            if (pl != null && pr != null) {
                //s 当前节点的右子节点的最左边的子节点
                TreeNode<K,V> s = pr, sl;
                //一直向左遍历得到最左的子节点
                while ((sl = s.left) != null) // find successor
                    s = sl;
                //把当前节点的颜色和最左的子节点颜色互换
                boolean c = s.red; s.red = p.red; p.red = c; // swap colors
                //sr s的右子节点
                TreeNode<K,V> sr = s.right;
                //pp 当前节点p的父节点
                TreeNode<K,V> pp = p.parent;
                if (s == pr) { // p was s's direct parent
                    //如果s等于当前节点的右子节点,这句表示pr没有左子节点
                    //把p作为s的右子节点
                    p.parent = s;
                    s.right = p;
                }
                else {
                    //其他情况,pr存在左子节点
                    //sp s的父节点
                    TreeNode<K,V> sp = s.parent;
                    //p的父节点指向sp
                    if ((p.parent = sp) != null) {
                        //如果sp不为null,表示pr不为null,即p存在右子节点
                        if (s == sp.left)
                            //如果s是父节点的左子节点
                            //把sp的左子节点设置为当前节点
                            sp.left = p;
                        else
                            //如果s是父节点的右子节点
                            //把sp的右子节点设置为当前节点
                            sp.right = p;
                    }
                    //把pr作为s的右子节点
                    if ((s.right = pr) != null)
                        //如果pr不为null,把pr的父节点指向s
                        pr.parent = s;
                }
                //把当前节点的左节点置为null
                p.left = null;
                //把当前节点的右子节点指向s的右子节点
                if ((p.right = sr) != null)
                    //如果当前节点的右子节点不为null
                    //把s的右子节点的父节点指向当前节点
                    sr.parent = p;
                //把当前节点的左子节点设置为s的左子节点
                if ((s.left = pl) != null)
                    //如果当前节点的左子节点不为null
                    //把当前节点的左子节点的父节点指向s节点
                    pl.parent = s;
                //当把s节点的父节点设置为前节点的父节点
                if ((s.parent = pp) == null)
                    //如果当前节点的父节点为null
                    //s节点为根节点
                    root = s;
                else if (p == pp.left)
                    //如果当当前节点为父节点的左子节点
                    //把当前节点的父节点的左子节点设置为s节点
                    pp.left = s;
                else
                    //把当前节点的父节点的右子节点设置为s节点
                    pp.right = s;
                if (sr != null)
                    //如果s节点的右子节点不为null
                    //需要替换s节点的右子节点
                    replacement = sr;
                else
                    //如果s节点的右子节点为null
                    //需要替换的节点为当前节点
                    replacement = p;
            }
    //******************第二种情况 删除的节点只有左子树***********
            else if (pl != null)
                //如果当前节点的右子节点为空,删除的节点只有左子树
                //替换当前节点的左子节点
                replacement = pl;
    //******************第三种情况 删除的节点只有右子树***********
            else if (pr != null)
                //如果当前节点的左子节点为空
                //替换当前节点的右子节点
                replacement = pr;
    //******************第三种情况 删除的节点是叶子节点***********
            else
                //如果当前节点的左右子节点都为null
                //替换当前节点
                replacement = p;
    //------------------------------end--------------------------------
    
    		//****************替换的节点不是当前节点,表示只有左子节点或者右子节点***************
    		//****************直接使用replacement替换p***************
            if (replacement != p) {
                //如果要替换的节点不是当前节点
                //pp 当前节点的父节点
                //被替换节点的父节点指向当前节点父节点
                TreeNode<K,V> pp = replacement.parent = p.parent;
                if (pp == null)
                    //当前节点的父节点为null
                    //根节点指向要被替换的节点
                    root = replacement;
                else if (p == pp.left)
                    //如果当前节点是当前节点父节点的左子节点
                    //当前节点的父节点的左子节点指向要被替换的节点
                    pp.left = replacement;
                else
                    //如果当前节点是当前节点父节点的右子节点
                    //当前节点的父节点的右子节点指向要被替换的节点
                    pp.right = replacement;
                //当前节点的左子节点 右子节点 父节点都置为null
                //移除当前节点
                p.left = p.right = p.parent = null;
            }
    		//****************end***************
    
		    //如果当前节点是红节点 r指向根节点
    		//如果当前节点是黑节点 r指向删除后重新平衡的根节点
            TreeNode<K,V> r = p.red ? root : balanceDeletion(root, replacement);
		
    		//*********************移除当前节点*****************
            if (replacement == p) {  // detach
                //如果被替换的节点是当前节点
                //pp 当前节点的父节点副本
                TreeNode<K,V> pp = p.parent;
                //当前解得父节点引用设置为null
                p.parent = null;
                if (pp != null) {
                    //如果当前节点的父节点不为null
                    if (p == pp.left)
                        //如果当前节点是当前节点父节点的左子节点
                        //把当前节点父节点的左子节点引用置为null
                        pp.left = null;
                    else if (p == pp.right)
                        //如果当前节点是当前节点父节点的右子节点
                        //把当前节点父节点的右子节点引用置为null
                        pp.right = null;
                }
            }
    		//*********************end*****************
    
            if (movable)
                //如果movable为true
                //把删除后重新平衡的根节点放到插槽的第一个位置
                moveRootToFront(tab, r);
        }

treeify 方法

//从链表转换为红黑树
final void treeify(Node<K,V>[] tab) {
    		//保存根节点,也是tab对应插槽的第一个节点
            TreeNode<K,V> root = null;
    		//for循环遍历整个链表,只要还有下一个节点就继续遍历
    		//next保存当前遍历节点的下一个节点
            for (TreeNode<K,V> x = this, next; x != null; x = next) {
                //获取当前节点的下一个节点
                next = (TreeNode<K,V>)x.next;
                //把当前节点的左右子节点都置为null
                x.left = x.right = null;
                if (root == null) {
                    //如果根节点为null
                    //把当前节点置为根节点,并将颜色设置为黑色,红黑树根节点必须为黑色
                    //把当前节点的父节点引用置为null,根节点没有父节点
                    x.parent = null;
                    x.red = false;
                    root = x;
                }
                else {
                    //获取当前节点的key
                    K k = x.key;
                    //获取当前节点的hash值
                    int h = x.hash;
                    Class<?> kc = null;
                    //内层循环的意义在于插入外层循环的节点到红黑树中
                    //初始化时,把p指向根节点,进行循环操作,循环中p表示内层循环的当前节点
                    for (TreeNode<K,V> p = root;;) {
                        //dir 小于0指向左子节点,大于0指向右子节点
                        //ph 存储p的hash值
                        int dir, ph;
                        //获取p的key
                        K pk = p.key;
                        if ((ph = p.hash) > h)
                            //如果p的hash值大于当前节点的hash值,往左
                            dir = -1;
                        else if (ph < h)
                            //如果p的hash值小于当前节点的hash值,往右
                            dir = 1;
                        //比较p节点的key和当前节点的key
                        else if ((kc == null &&
                                  (kc = comparableClassFor(k)) == null) ||
                                 (dir = compareComparables(kc, k, pk)) == 0)
                            dir = tieBreakOrder(k, pk);
					  //xp 存储p节点的副本
                        TreeNode<K,V> xp = p;
                        //dir大于0 p指向p的右子节点; dir小于等于0 p指向p的左子节点
                        //找到对应的位置,并且p节点对应的子节点为空,进行插入
                        if ((p = (dir <= 0) ? p.left : p.right) == null) {
                            //把p节点的副本设置为当前节点的父节点
                            x.parent = xp;
                            if (dir <= 0)
                                //如果dir小于等于0 当前节点作为p节点的左子节点
                                xp.left = x;
                            else
                                //如果dir大于0 当前节点作为p节点的右子节点
                                xp.right = x;
                            //插入后进行红黑树平衡操作,并重新调整根节点
                            root = balanceInsertion(root, x);
                            //退出循环
                            break;
                        }
                        //如果当前节点对应p节点的左子节点或者右子节点不为空,把p设置为对应的子节点继续循环遍历
                    }
                }
            }
    	   //把最后得到的根节点插入对应插槽的第一个位置
            moveRootToFront(tab, root);
        }

balanceInsertion 方法

//插入x节点后进行平衡操作
static <K,V> TreeNode<K,V> balanceInsertion(TreeNode<K,V> root,
                                                    TreeNode<K,V> x) {
    		//默认插入的是红节点
            x.red = true;
    	     //for循环遍历
    		 //xp 存储x节点的父节点
             // xpp 存储x节点的祖父节点,父节点的父节点
    		//xppl 存储x节点的左叔叔节点,父节点的父节点的左子节点
    		//xppr 存储x节点的右叔叔节点,父节点的父节点的右子节点
            for (TreeNode<K,V> xp, xpp, xppl, xppr;;) {
                if ((xp = x.parent) == null) {
                    // 如果父节点为空,表示当前节点为根节点
                    //把当前节点设置为黑节点,并返回
                    x.red = false;
                    return x;
                }
                else if (!xp.red || (xpp = xp.parent) == null)
                    //如果父节点为黑节点 或者 祖父节点为null
                    //直接返回root节点,不需要修改
                    return root;
                	//以下情况需要旋转个染色达到新的平衡
                &emsp;&emsp;//1.插入节点的父节点和其叔叔节点(祖父节点的另一个子节点)均为红色。操作:将当前节点的父节点和叔叔节点涂黑,将祖父节点涂红,再将当前节点指向其祖父节点,再次从新的当前节点开始算法
&emsp;&emsp;				//2.插入节点的父节点是红色的,叔叔节点是黑色的,且插入节点是其父节点的右子节点。操作:将当前节点的父节点作为新的节点,以新的当前节点为支点做左旋操作
&emsp;&emsp;				//3.插入节点的父节点是红色的,叔叔节点是黑色的,且插入节点是其父节点的左子节点。操作:将当前节点的父节点涂黑,将祖父节点涂红,在祖父节点为支点做右旋操作。最后把根节点涂黑,整个红-黑树重新恢复了平衡
                if (xp == (xppl = xpp.left)) {
                    //如果父节点是祖父节点的左子节点
                    if ((xppr = xpp.right) != null && xppr.red) {
                        //如果右叔叔节点不为null并且右叔叔节点为红节点
                        //右叔叔节点设置为黑节点
                        xppr.red = false;
                        //父节点设置为黑节点
                        xp.red = false;
                        //祖父节点设置为红节点
                        xpp.red = true;
                        //把当前节点指向祖父节点,然后进行下一次循环
                        x = xpp;
                    }
                    else {
                        //其他情况,右叔叔节点为null或者右叔叔节点是黑节点
                        if (x == xp.right) {
                            //如果当前节点是父节点的右子节点
                            //当前节点指向父节点并且作为基础节点进行左旋
                            root = rotateLeft(root, x = xp);
                            //重新为祖父节点赋值
                            xpp = (xp = x.parent) == null ? null : xp.parent;
                        }
                        if (xp != null) {
                            //如果父节点不为空
                            //设置父节点为黑节点
                            xp.red = false;
                            if (xpp != null) {
                                //如果祖父节点不为空
                                //设置祖父节点为红节点
                                xpp.red = true;
                                //祖父节点作为基础节点进行右旋
                                root = rotateRight(root, xpp);
                            }
                        }
                    }
                }
                else {
                    //其他情况,如果父节点是祖父节点的右子节点
                    if (xppl != null && xppl.red) {
                        //如果左叔叔节点不为null并且左叔叔节点是红节点
                        //左叔叔节点设置为黑节点
                        xppl.red = false;
                        //父节点设置为黑节点
                        xp.red = false;
                        //祖父节点设置为红节点
                        xpp.red = true;
                        //当前节点指向祖父节点,然后进行下一次循环
                        x = xpp;
                    }
                    else {
                        //其他情况,如果左叔叔节点不为null或者左叔叔节点是黑节点
                        if (x == xp.left) {
                            //如果当前节点是父节点的左子节点
                            //当前节点指向父节点并作为基础节点进行右旋
                            root = rotateRight(root, x = xp);
                            //重新为祖父节点赋值
                            xpp = (xp = x.parent) == null ? null : xp.parent;
                        }
                        if (xp != null) {
                            //如果父节点不为null
                            //父节点设置为黑节点
                            xp.red = false;
                            if (xpp != null) {
                                //如果祖父节点不为null
                                //祖父节点设置为红节点
                                xpp.red = true;
                                //祖父节点作为基础节点进行左旋
                                root = rotateLeft(root, xpp);
                            }
                        }
                    }
                }
            }
        }

分析:

  • 情形一:如果当前节点的父节点是祖父节点的左子节,右叔叔节点为红节点

    • 进行染色,把父节点和右叔叔节点染色为黑节点,祖父节点染色为红节点
    • 把祖父节点设置为当前节点继续进行平衡操作
  • 情形二:如果当前节点的父节点是祖父节点的左子节,右叔叔节点为黑节点

    • 如果当前节点是父节点的右子节点

      • 把当前节点指向父节点

      • 以当前节点的父节点为基础节点进行左旋

      • 重新计算父节点和祖父节点

    • 如果父节点不为空

      • 把父节点染色为黑节点

      • 如果祖父节点不为空

        • 把祖父节点染色为红节点
        • 以祖父节点为基础节点进行右旋
  • 情形三:如果父节点是祖父节点的右子节点,左叔叔点为红节点

    • 进行染色,把左叔叔节点染色为黑节点,父节点染色为黑节点,祖父节点染色为红节点
    • 把祖父节点置为当前节点继续进行平衡操作
  • 情形四:如果父节点是祖父节点的右子节点,左叔叔节点为黑节点

    • 如果当前节点是父节点的左子节点
      • 把当前节点指向父节点
      • 以当前节点的父节点为基础节点进行右旋
      • 重新计算当前节点的父节点和祖父节点
    • 如果父节点不为空
      • 把父节点染色为黑节点
      • 如果祖父节点不为空
        • 把祖父节点染色为红节点
        • 以祖父节点为基础节点进行左旋

rotateLeft 方法

//左旋
/**
 * 左旋示意图:对节点x进行左旋 
 *     p                       p 
 *    /                       / 
 *   x                       y 
 *  / \                     / \ 
 * lx  y      ----->       x  ry 
 *    / \                 / \ 
 *   ly ry               lx ly 
 */
static <K,V> TreeNode<K,V> rotateLeft(TreeNode<K,V> root,
                                              TreeNode<K,V> p) {
    		//r 存储当前节点的右子节点
    		//rl 存储当前的右子节点的左子节点
    		//pp 存储当前节点父节点
            TreeNode<K,V> r, pp, rl;
            if (p != null && (r = p.right) != null) {
                //如果当前节点不为null 并且 当前节点的右子节点不为null
                //当前节点的右子节点指向当前节点的右子节点的左子节点
                if ((rl = p.right = r.left) != null)
                    //rl的父节点指向当前节点
                    rl.parent = p;
                //当前节点的右子节点的父节点指向当前节点的父节点
                if ((pp = r.parent = p.parent) == null)
                    //如果r的父节点为空,把r设置为根节点,并设置为黑节点
                    (root = r).red = false;
                else if (pp.left == p)
                    //如果当前节点是父节点的左子节点
                    //当前节点的父节点的左子节点指向当前节点的右子节点
                    pp.left = r;
                else
                    //如果当前节点是父节点的右子节点
                    //当前节点的父节点的右子节点指向当前节点的右子节点
                    pp.right = r;
                //当前节点的右子节点的左子节点指向当前节点
                r.left = p;
                //当前节点的父节点指向当前节点的右子节点
                p.parent = r;
            }
    		//返回根节点
            return root;
        }

左旋:

  • 当前节点p的右子节点pr作为当前节点的父节点
  • 当前节点p作为当前节点右子节点pr的左子节点
  • 当前节点的右子节点pr的左子节点prl作为当前节点p的右子节点

rotateRight 方法

//右旋
/**
 * 右旋旋示意图:对节点y进行右旋
 *        p                   p
 *       /                   /
 *      y                   x
 *     / \                 / \
 *    x  ry   ----->      lx  y
 *   / \                     / \
 * lx  rx                   rx ry
 */
static <K,V> TreeNode<K,V> rotateRight(TreeNode<K,V> root,
                                               TreeNode<K,V> p) {
    		//l 当前节点的左子节点
    		//当前节点的左子节点的右子节点
            TreeNode<K,V> l, pp, lr;
            if (p != null && (l = p.left) != null) {
                //如果当前节点p不为null 并且 当前节点p的左子节点l不为null
                //当前节点p的左子节点l指向当前节点p的左子节点l的右子节点lr
                if ((lr = p.left = l.right) != null)
                    //如果当节点的左子节点的右子节点不为null
                    //当前节点的左子节点的右子节点指向当前节点
                    lr.parent = p;
                //当前节点的左子节点的父节点指向当前节点的父节点
                if ((pp = l.parent = p.parent) == null)
                    //如果当前节点的父节点为null
                    //根节点指向当前节点左子节点
                    //把当前节点的左子节点置为黑节点
                    (root = l).red = false;
                else if (pp.right == p)
                    //如果当前节点是父节点的右子节点
                    //当前节点的父节点的右子节点指向当前节点的左子节点
                    pp.right = l;
                else
                    //其他情况,当前节点的父节点的左子节点指向当前节点的坐子节点
                    pp.left = l;
                //当前节点的左子节点的右节点指向当前节点
                l.right = p;
                //当前节点的父节点指向当前节点的左子节点
                p.parent = l;
            }
    		//返回根节点
            return root;
        }

右旋:

  • 当前节点p的左子节点pl作为当前节点p的父节点
  • 当前节点p作为当前节点的左子节点pl的右子节点
  • 当前节点p的的左子节点pl的右子节点plr作为当前节点的左子节点

balanceDeletion 方法

//删除平衡
static <K,V> TreeNode<K,V> balanceDeletion(TreeNode<K,V> root,
                                                   TreeNode<K,V> x) {
    		//最外层是一个无限for循环,不断调整x的位置,直到整棵树达到平衡了才中断循环
    		//xp x的父节点
    		//xpl x父节点的左子节点 左兄弟节点
    		//xpr x父节点的右子节点 右兄弟节点
            for (TreeNode<K,V> xp, xpl, xpr;;)  {
                if (x == null || x == root)
                    //如果x是null或者是根节点直接返回根节点
                    return root;
                else if ((xp = x.parent) == null) {
                    //如果x的父节点为null,表示当前节点是根节点
                    //把当前节点置为黑节点,并返回
                    x.red = false;
                    return x;
                }
                else if (x.red) {
                    //如果x节点是红节点
                    //把x节点染色为黑节点,并返回根节点
                    x.red = false;
                    return root;
                }
                else if ((xpl = xp.left) == x) {
                    //如果x是父节点的左子节点
                    if ((xpr = xp.right) != null && xpr.red) {
                        //x的右兄弟节点不为null 并且 右兄弟节点为红节点
                        //把右兄弟节点染色为黑节点
                        xpr.red = false;
                        //父节点染色为红节点
                        xp.red = true;
                        //以父节点为基础节点进行左旋
                        root = rotateLeft(root, xp);
                        //重新为xp和xpr赋值
                        xpr = (xp = x.parent) == null ? null : xp.right;
                    }
                    if (xpr == null)
                        //如果x的右兄弟节点不存在
                        //x指向父节点
                        x = xp;
                    else {
                        //右兄弟节点存在的情况
                        //sl 右兄弟节点的左子节点
                        //sr 右兄弟节点的右子节点
                        TreeNode<K,V> sl = xpr.left, sr = xpr.right;
                        if ((sr == null || !sr.red) &&
                            (sl == null || !sl.red)) {
                            //如果右兄弟的子节点为空或者为黑节点
                            //把右兄弟节点染色为红节点
                            xpr.red = true;
                            //x指向父节点
                            x = xp;
                        }
                        else {
                            //右兄弟节点不全为null并且为红节点的情况
                            if (sr == null || !sr.red) {
                                //如果右兄弟节点的右子节点为null或者为黑节点
                                if (sl != null)
                                    //如果右兄弟节点的左子节点不为null
                                    //把右兄弟节点的左子节点染色为黑节点
                                    sl.red = false;
                                //把右兄弟节点染色为红节点
                                xpr.red = true;
                                //以右兄弟节点为基础节点进行右旋
                                root = rotateRight(root, xpr);
                                //重新设置x的父节点和右兄弟节点
                                xpr = (xp = x.parent) == null ?
                                    null : xp.right;
                            }
                            if (xpr != null) {
                                //如果右兄弟节点不为null
                                //如果父节点为null 右兄弟节点染色为黑节点
                                //如果父节点不为null 右兄弟节点染色为父节点的颜色
                                xpr.red = (xp == null) ? false : xp.red;
                                if ((sr = xpr.right) != null)
                                    //如果右兄弟节点的右子节点不为null
                                    //右兄弟节点的右子节点染色为黑节点
                                    sr.red = false;
                            }
                            if (xp != null) {
                                //如果父节点不为null
                                //父节点染色为黑节点
                                xp.red = false;
                                //以父节点为基础节点进行左旋
                                root = rotateLeft(root, xp);
                            }
                            //把x节点指向根节点
                            x = root;
                        }
                    }
                }
                else { //对称性,如果x节点是父节点的右子节点
                    if (xpl != null && xpl.red) {
                        xpl.red = false;
                        xp.red = true;
                        root = rotateRight(root, xp);
                        xpl = (xp = x.parent) == null ? null : xp.left;
                    }
                    if (xpl == null)
                        x = xp;
                    else {
                        TreeNode<K,V> sl = xpl.left, sr = xpl.right;
                        if ((sl == null || !sl.red) &&
                            (sr == null || !sr.red)) {
                            xpl.red = true;
                            x = xp;
                        }
                        else {
                            if (sl == null || !sl.red) {
                                if (sr != null)
                                    sr.red = false;
                                xpl.red = true;
                                root = rotateLeft(root, xpl);
                                xpl = (xp = x.parent) == null ?
                                    null : xp.left;
                            }
                            if (xpl != null) {
                                xpl.red = (xp == null) ? false : xp.red;
                                if ((sl = xpl.left) != null)
                                    sl.red = false;
                            }
                            if (xp != null) {
                                xp.red = false;
                                root = rotateRight(root, xp);
                            }
                            x = root;
                        }
                    }
                }
            }
        }

小结

红黑树现在了解的还不是很透彻,等后面数据结构专题再细说红黑树。数据结构最好还是结合图来说明更好

  • 红黑树是二叉平衡树,通过变色左旋右旋达到平衡
  • TreeNode是红黑树的结构,但也保留了链表的特性,这样适合快速转换为链表结构