红黑树应用之HashMap的TreeNode源码解析

入场

  • 自从JDK1.8之后,HashMap就引入了红黑树,之前是数组+链表的结构,之后就是数组+链表+红黑树的结构,事先声明一下对于HashMap刚了解的不适合看此篇文章,可以去看看其他比较全的介绍HashMap文章,在此就不多说了,此篇主要讲HashMap的TreeNode的左右旋的源码解析,主要自己对别人写得理解很迷糊,想自己记录下自己当时的理解,以后便于自己和大家更好的理解,话不多说,源码走起!

为什么要引入红黑树

  • 在HashMap是数组+链表的结构时会有一些缺点,会出现一个链表特别多的结构形式,这样就显得检索性能很差,所以引入红黑树,当链表的节点等于8个时,就会转化为红黑树的结构形式;当节点等于6个时,红黑树转化为链表的结构形式,至于为什么不是7,个人觉得,相差一个起到缓冲作用吧,不然频繁转化也耗性能。

红黑树的左旋源码解析

  • 介绍一下红黑树的五个性质(其实就是满足红黑树的条件):

    • 性质1. 节点只有红色或者黑色的。
    • 性质2. 根节点必须是黑色。
    • 性质3. 当子节点是NIL节点或者为null时,节点必须是黑色。
    • 性质4. 每个红色节点必须有两个黑色的子节点(从每个叶子到根的所有路径上不能有两个连续的红色节点);每个黑色节点的子节点可以是黑色或者红色节点。
    • 性质5. 从任一节点到其每个叶子的所有简单路径都包含相同数目的黑色节点。
  • 以下是HashMap的TreeNode的左旋方法rotateLeft源码

        /**
         * 左旋转
         *
         * @param root
         * @param p
         * @param <K>
         * @param <V>
         * @return
         */
        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.parent = p;
                if ((pp = r.parent = p.parent) == null)
                    (root = r).red = false;
                else if (pp.left == p)
                    pp.left = r;
                else
                    pp.right = r;
                r.left = p;
                p.parent = r;
            }
            return root;
        }
        

肯定你会问为什么要左旋方法?这个就是满足上述的五个性质而作出的调整,也是红黑树的实现,之前红黑树叫二叉平衡B树,就是在二叉树的基础进行优化,和平衡树很像,防止一棵树的一边过于枝繁叶茂,主要检索性能深度过于深,使之平衡。

源码解读(主要结合图去看,更便于理解):
rotateLeft两个传入的参数root、p;
* 参数p是旋转点
* 参数root是旋转点p的父节点
方法里面的局部变量参数r、pp、rl,起到暂时存值,方便交换赋值;
* 参数r将被赋值于旋转点p的右节点的值,在整个方法里就可以理解为旋转点p的右节点,其他都可以这么去理解
* 参数pp是旋转点p的父节点,其实和传入的参数root是一个意思,只不过新new节点进行赋值
* 参数rl是旋转点p的右节点的左节点
代码

        p != null && (r = p.right) != null

中对于旋转点p的先判断如果 p 是否 null , 其次判断p的右节点是否null , 这里对于&&的用法还是要注意一下,首先对于p 是 null 肯定直接返回root了,然后对于p的右节点是null 也是直接返回root,如果p的右节点都没有,还左旋个毛线啊。接着继续往下走代码,接下来是两个并行的if判断,先看第一个if判断代码

  		if ((rl = p.right = r.left) != null)
                rl.parent = p;

中将旋转点p的右节点r的左节点赋值给旋转点p的右节点即为rl,如果rl是null并不是直接返回,还要继续往下走第二个if判断代码

  		   if ((pp = r.parent = p.parent) == null)
                (root = r).red = false;
            else if (pp.left == p)
                pp.left = r;
            else
                pp.right = r;

中首先将旋转点p的父节点赋值给r的父节点即为pp(其实这地方就是发生左旋,就是将旋转点p的右节点r变成旋转点位置),如果当pp是null时,则说明父节点root就是null,r就是父节点,并且是根节点,则必须是黑色,满座上述性质2,然后返回root;如果pp不为null时继续往下走else
if 判断,其实原先旋转点p并不知道它是左节点还是右节点,如果原先旋转点p是左,则左旋之后,新的旋转点r则也要是左,反之则右。即使上面两个if判断都为null也没有关系,还是需要发生左旋操作的,继续走最后两行代码

  		    r.left = p;
            p.parent = r;

中这两行就是交换,新旋转点变成r,p节点变成r节点的左节点,并且r节点也变成p节点的父节点了,其实和排序有异曲同工之写法。右旋方法和左旋一样,只是左右关系,就不源码解析了。

思考

HashMap什么操作需要左旋或者右旋,其实是删除操作时候,后续再做分享。。。

对于上述大家疑惑或者知错纠正地方,欢迎留言评论,哈哈哈,终于写了第二篇博客。。。。还差个图没贴

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值