HashMap的工作原理和底层实现(二)红黑树的左旋、右旋

HashMap中的红黑树左旋、右旋


摘要:

    HashMap是java最常用的容器之一,本文会通过阅读源码的方式来理解HashMap中是如何进行红黑树的左旋和右旋

一、什么是左旋和右旋


        红黑树的性质

  • 每个节点要么是黑色,要么是红色。
  • 根节点是黑色。
  • 每个叶子节点(NIL)是黑色。
  • 每个红色结点的两个子结点一定都是黑色。
  • 任意一结点到每个叶子结点的路径都包含数量相同的黑结点。

        每当对红黑树结点进行增删改的时候可能会破坏红黑树的性质,因此我们需要维持这5条性质。有三种方式:变色、左旋、右旋.变色简单,本文会根据HashMap的源码分析左旋和右旋

1)左旋

        以结点p作为支点进行左旋,其左子结点不变,右子结点变为p的右子节点的左子结点,且p的父结点变为左子结点。

        支点为p,其父结点为pp,右子结点和左子结点为r和l,r的左右孩子分别为rl和rr。则示意图如下:

HashMap的工作原理和底层实现(二)红黑树的左旋、右旋_第1张图片

2)右旋

        以结点p作为支点右旋,其右子结点不变,左子结点变为其左子结点的右子结点,且p的父结点变为右子结点。

        如图所示

HashMap的工作原理和底层实现(二)红黑树的左旋、右旋_第2张图片

二、HashMap对左旋右旋的实现


(1)左旋

        源码如下

       //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) {   //如果p不为空且存在右子结点r
                if ((rl = p.right = r.left) != null)    //判断右子结点的左子结点rl存在
                    rl.parent = p;                      //存在设置rl的父节点为p
                if ((pp = r.parent = p.parent) == null) //判断p的父节点pp是否存在
                    (root = r).red = false;            //如果不存在设置新的根节点为r且黑色
                else if (pp.left == p)                 //父结点pp存在且p为pp的左子结点
                    pp.left = r;
                else                                   //父结点pp存在且p为pp的左子结点
                    pp.right = r;
                r.left = p;
                p.parent = r;
            }
            return root;
        }

形参root为红黑树的根节点,因为左旋和右旋可能会改变根节点,p为旋转的结点,r为p的右子节点,pp为【的父节点,rl为r的左子结点。返回值为根结点.旋转分如下步骤

  • 1.如果结点p为空或者p不存在右子结点r(此时完成不了旋转),则直接返回,否则继续如下步骤.
  • 2.如果rl不为空,则使p的右边等于rl。否则如果rl为空则不用操作,直接下一步
  • 3.如果p没有父结点,即他本身就是根节点,那么设置r为根节点,并设置r为黑色(红黑树的性质2).如果p父结点存在且p为父结点的左子结点,则父结点的左子结点设置为r。如果p父结点存在且p为父结点的右子节点,则父结点的右子结点设置为r。
  • 4.最后把r的左子节点设置为p,p的父结点设置为r
(2)右旋

​ 右旋和左旋类似

        源码如下

       //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) {    //如果p不为空且存在左子结点l
                if ((lr = p.left = l.right) != null)    //判断左子结点的右子结点lr存在
                    lr.parent = p;                      //存在则设置rl的父结点为p
                if ((pp = l.parent = p.parent) == null) //判断p的父结点pp是否存在
                    (root = l).red = false;          //如果不存在设置新的根节点为l且l为黑色
                else if (pp.right == p)              //父结点pp存在且p为pp的右子结点
                    pp.right = l;
                else                                 //父结点pp存在且p为pp的左子结点
                    pp.left = l;
                l.right = p;
                p.parent = l;
            }
            return root;
        }

p为旋转的结点,l为p的左子节点,pp为p的父节点,lr为l的右子结点。返回值为根结点.旋转分如下步骤

  • 1.如果结点p为空或者p不存在左子结点r(此时完成不了旋转),则直接返回,否则继续如下步骤.
  • 2.如果lr不为空,则使p的左边等于lr。否则如果lr为空则不用操作,直接下一步
  • 3.如果p没有父结点,即他本身就是根节点,那么设置l为根节点,并设置l为黑色(红黑树的性质2).如果p父结点存在且p为父结点的左子结点,则父结点的左子结点设置为l。如果p父结点存在且p为父结点的右子节点,则父结点的右子结点设置为l。
  • 4.最后把l的左子节点设置为p,p的父结点设置为l

你可能感兴趣的:(java,数据结构与算法)