白话:红黑树及源码的套路(上)

2019-07-31  本文已影响0人  瑞瑞余之

上一节聊到HashMap,我们在读源码的过程中,发现HashMap在JDK1.8之后对链表部分的实现进行了改进,从源码不难看出,当HashMap中某一个链表长度超过阈值(TREEIFY_THRESHOLD = 8 )之后,就会将该链表树化。当我们看到内部类TreeNode定义时,会看到该树类型为红黑树——red-black tree。
本节就结合HashMap中红黑树的源码,具体梳理一下红黑树和它如何与HashMap结合。以下为文章路径:

HashMap为什么从链表换成了树

上一节我们在阅读源码的时候,发现这样一句话:

 if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
       treeifyBin(tab, hash);

当链表节点的计数超过TREEIFY_THRESHOLD - 1则将该链表树化,为什么要这样呢?其实比较一下链表和树的优缺点就能大致明白该优化的目的。我们假设一条链表上有10个节点,在查询时,最坏情况需要查询10次,N(10)。对于树而言不同的树复杂度不同,但是对于最基本的二叉树:左子树一定比root小,右子树一定比root大,相当于是通过二分法在进行查找,查询速度绝大部分时候比链表要快。但是一般人们理解的二叉树(又叫二叉搜索书 BST)会出现一个问题,正常情况它是这样的:


普通BST

但是也有可能出现这样一种情况:树的节点正好从大到小的插入,此时树的结构也类似于链表结构,这时候的查询或写入耗时与链表相同:


特殊BST
为了避免这种特殊的情况发生,引入了平衡二叉树(AVL)和红黑树(red-black tree)。它们都是通过本身的建树原则来控制树的层数和节点位置,因为rbtree是由AVL演变而来,所以我们从了解AVL开始。

从平衡二叉树到红黑树

  1. 平衡二叉树
    平衡二叉树也叫AVL(发明者名字简写),也属于二叉搜索树的一种,与其不同的是AVL通过机制保证其自身的平衡。
    平衡二叉树的原则有以下几点:

场景3 左右结构:

左右结构

场景4 右左结构:

右左结构

可见无论哪种情况的失衡,都可以通过旋转来调整。不难看出,旋转在图上像是将pivot节点向上提(将它提升为root节点),而后两边的节点会物理的分布在新root节点的两边,接下来按照二叉树的要求:左子树小于root,右子树大于root进行调整。从图左左结构可以看出,当右旋时原来pivot(7)的右子树会转变到用root点(9)的左子树处;从图右右结构可见,当左旋时,原来pivot(18)的左子树会分布到原root点(9)的右子树。
对于左右结构和右左结构无非是经过多次旋转达到稳定,旋转的方式并没有区别,所以总结下来:


AVL树平衡总结

既然AVL树可以保证二叉树的平衡,这就意味着它最坏情况的时间复杂度O(logn) 要低于普通二叉树和链表的最坏情况O(n)。那么HashMap就直接使用AVL树来替换链表就好了,为什么选择用红黑树呢?
我们会发现,由于AVL树必须保证Max(最大树高-最小树高) <= 1所以在插入的时候很容易出现不平衡的情况,一旦这样,就需要进行旋转以求达到平衡。正是由于这种严格的平衡条件,导致需要花大量时间在调整上,故AVL树一般使用场景在于查询而弱于增加删除。红黑树继承了AVL可自平衡的优点,同时在查询速率和调整耗时中寻找平衡,放宽了树的平衡条件,在实际应用中,红黑树的使用要多得多。

  1. 红黑树(RBTree)
    红黑树也是一种自平衡二叉查找树,它与AVL树类似,都在添加和删除的时候通过旋转操作保持二叉树的平衡,以求更高效的查询性能。与AVL树相比,红黑树牺牲了部分平衡性以换取插入/删除操作时少量的旋转操作,整体来说性能要优于AVL树。
    红黑树的原则有以下几点:

在平衡的过程中,要注意红黑树的规定原则。插入红节点,不能仅仅将父节点由红变黑,因为这样会增加这条支路的黑节点数,从而违反“从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点”。将父节点和叔叔节点都变黑,再将祖父节点由黑变红,这样一来,以13为root的红黑树对外黑色节点数没变,对内各条支路节点数一致。
场景4 父节点为红色,叔叔节点为黑色且新节点为右子树:

左旋将右子树变为左子树

节点8的父节点为红,叔叔节点为黑且,通过左旋的方式,让整个情况变成下一个场景:父节点红色,叔叔节点为黑色且新节点为左子树。
场景5 父节点为红色,叔叔节点为黑色且新节点为左子树:

完成BRTree平衡
这五个场景就可以涵盖所有的红黑树未平衡的场景,总结起来就是:
红黑树总结
本文从概念上介绍了红黑树及其前辈AVL树,已经它们自平衡的方式和过程,从下一篇开始我们会对红黑树源码进行阅读,加深其理解。
上一篇 下一篇

猜你喜欢

热点阅读