当前位置: 代码迷 >> 综合 >> 数据结构学习之树与红黑树(java1.8 hashMap底层实现)
  详细解决方案

数据结构学习之树与红黑树(java1.8 hashMap底层实现)

热度:15   发布时间:2024-01-30 05:03:00.0

数据结构学习之树与红黑树

写在前头,最近在看java1.8的HashMap源码,被里面的红黑树折磨的死去活来的,又是插入,又是平衡,左旋右旋的,真的头大。特意静下心来学习红黑树的特性!!!

概念

树(tree)是一种抽象的数据类型,用来模拟具有树状结构性质的数据集合,它是由n(n>0)个有限节点通过连接它们的边组成一个具有层次关系的集合。把它叫做树是因为它看起来像一颗倒挂的树,也就是说它根朝上,而叶朝下的。

树有很多种,向上的一个节点有多于两个子节点的树叫做多路数,而每个节点最多只有两个子节点的一种形式称为二叉树

常用术语解释

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9vj1HNMO-1595153488467)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200719103558046.png)]

  • 路径:顺着节点的边从一个节点到另一个节点,所经过的节点的顺序排列就成为路径
  • 根:树顶端的节点称为根,一棵树只有一个根
  • 父节点:若一个节点含有子节点,则这个节点称为其子节点的父节点
  • 子节点:一个节点含有的子树的节点称为该节点的子节点
  • 兄弟节点:具有相同父节点的节点互称为兄弟节点
  • 叶节点:没有子节点的节点称为叶节点,也叫叶子节点。
  • 子树:每个节点都可以作为子树的根,它和它所有的子节点、子节点的子节点等都包含在子树中。
  • 节点的层次:从根开始定义,根为第一层,根的子节点为第二层,以此类推。
  • 深度:对于任意节点n,n的深度为从根到n的唯一路径长,根的深度为0;(从上往下看)
  • 高度:对于任意节点n,n的高度为从n到一片树叶的最长路径长,所有树叶的高度为0;(从下往上看)

红黑树的性质

  • 每个节点要么是黑色,要么是红色
  • 根节点是黑色
  • 每个叶子节点(NIL)是黑色
  • 每个红色节点的两个子节点一定都是黑色。 不能有两个红色节点相连。
  • 任意一节点到每个叶子节点的路径都包含数量相同黑结点。俗称:黑高!黑色完美平衡

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YSNUz6nP-1595153488469)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200719111410402.png)]

红黑树并不是一个完美平衡二叉查找树,有时根结点的右子树显然比左子树高,但左子树和右子树的黑结点的层数是相等的,也即任意一个结点到到每个叶子结点的路径都包含数量相同的黑结点(性质5)。所以我们叫红黑树这种平衡为黑色完美平衡。

小结:红黑树的性质就是这些,只要一棵树满足以上条件,那么这棵树就是红黑树,并且是平衡状态的红黑树,这个东西千万不要去纠结为什么是这样,这是人家科学家辛辛苦苦研究好几年才定义出来的,你知道明白这样的定义就好,而且面试的时候不会问比这个层次更深的了。

前面说的红黑树的平衡,那么它靠的是什么呢? 重点来了:靠的是变色,左旋,右旋;这也是java1.8新特性hashMap中最经典的部分,理解完这部分,就能手撕hashMap了

  • 变色:节点的颜色由红变黑或由黑变红。
  • 左旋:以某个结点作为支点(旋转结点),其右子结点变为旋转结点的父结点,右子结点的左子结点变为旋转结点的右子结点,左子结点保持不变。
  • 右旋:以某个结点作为支点(旋转结点),其左子结点变为旋转结点的父结点,左子结点的右子结点变为旋转结点的左子结点,右子结点保持不变

什么?左旋右旋定义看不懂?晕圈??接下来再来看几张图,就懂了

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SusWSP8G-1595153488471)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200719112510631.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-K5ubPoEY-1595153488475)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200719112523437.png)]

红黑树查找

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7ImfoHpn-1595153488477)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200719112706905.png)]

查找的话没有什么说的,无非就是两两比较,每次比较都能去除一半的数据,所以效率高,时间复杂度是log2N

红黑树插入

插入操作包括两部分工作:

  • 查找插入的位置
  • 插入后自平衡(重点)

注意:插入节点,必须为红色,理由很简单,红色在父节点(如果存在)为黑色节点时,红黑树的黑色平衡没被破坏,不需要做自平衡操作。但如果插入结点是黑色,那么插入位置所在的子树黑色结点总是多1(破坏黑高),必须做自平衡。

插入的情况有多种,再讨论之前我们先来定义一下各个节点的叫法。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cfNzV7ki-1595153488478)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200719113440058.png)]

红黑树插入节点情景分析

情景一:红黑树为空树

最简单的一种情景,直接把插入结点作为根结点就行

注意:根据红黑树性质2:根节点是黑色。还需要把插入结点设为黑色。

情景二:插入节点的Key已存在

处理:更新当前节点的值,为插入节点的值,nodo.value=i.value

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3VsZOE8H-1595153488479)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200719114018738.png)]

情景三:插入节点的父节点是黑节点

由于插入的结点是红色的,当插入结点的黑色时,并不会影响红黑树的平衡,直接插入即可,无需做自平衡。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PVD4kdPG-1595153488480)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200719114220386.png)]

情景四:插入节点的父节点为红色

再次回想下红黑树的性质,根节点是黑色,如果插入节点的父节点是红色,那么该父节点不可能是根节点,所以插入节点总是存在爷爷节点,那么还要考虑到叔叔节点的存在情况,这点很重要,因为与后续的旋转操作有关。(叔叔节点可能为红色或者黑色、不存在)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yvBqCb8I-1595153488481)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200719114850817.png)]

情景四<1>:叔叔节点存在并且为红色

根据红黑树性质,根节点必定为黑色,红色节点不能两两相连,而此时该插入子树的红黑层树的情况是:黑红红(黑是根节点),显然最简单的处理方式是将其改为红黑红

处理:

  • 将P和U节点改为黑色
  • 将PP改为红色
  • 将PP设置为当前节点,进行后续处理

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BZeV4m8c-1595153488482)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200719120107527.png)]

可以看到,PP节点已经设为红色了,如果PP的父节点是黑色,那么无需做任何处理;

但如果PP的父节点是红色,则又一次违反了红黑树性质了,则我们只需要将PP设置为当前节点,继续做插入操作自平衡处理,直到平衡为止。

情景四<2>:叔叔节点不存在或为黑节点,并且插入节点的父节点是祖父节点的左子节点

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DqBN7ere-1595153488483)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200719162104368.png)]

情景四<2><1>:新插入节点,为父节点的左子节点(LL双红情况)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rvxOjYe7-1595153488485)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200719162353173.png)]

处理:

  • 变颜色,将P设置为黑色,将PP设置为红色
  • 对PP节点进行右旋

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2JRjys0U-1595153488486)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200719162751917.png)]

情景四<2><2>:新插入节点,为父节点的右子节点(LR红色情况)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gjWpCwaT-1595153488487)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200719162943758.png)]

处理:

  • 对P进行左旋
  • 将P设置为当前节点,得到LL双红的情况
  • 按照LL红色的情况处理(1、变颜色;2、右旋PP)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HWjd8IxS-1595153488487)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200719163214171.png)]

情景四<3>:叔叔节点不存在或者为黑节点,并且插入节点的父节点是爷爷节点的右子节点

该情景对应情景四<2>,只是方向反转。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mzprtjLY-1595153488488)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200719163528446.png)]

情景四<3><1>:新插入节点,为其父节点的右子节点(RR双红)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-voxZH7jN-1595153488489)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200719164127098.png)]

处理:

  • 变颜色:将P设置为黑色,将PP设置为红色
  • 对PP节点进行左旋

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DiSm07c9-1595153488490)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200719164346889.png)]

情景四<3><2>:新插入节点,为其父节点的左子节点(RL双红)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7r8Yi77i-1595153488491)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200719164610174.png)]

处理:

  • 对P进行右旋
  • 将P设置为当前节点,得到RR双红的情况
  • 按照RR红色情况处理(1、变颜色;2、左旋PP)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LUeN9MRM-1595153488492)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200719165003581.png)]

红黑树中和案例分析

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1guT7n5k-1595153488493)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200719165844497.png)]

简单分析:

1、找到插入的位置,如图1,发现存在叔叔节点18且为红色,那么需要将父节点8和叔叔节点18变为黑色,爷爷节点15变为红色,并且将爷爷节点15设置为当前插入节点进行后续操作,如图2

2、那么现在的插入节点是15,情况是叔叔节点30为黑色,并且是LR双红的情况,那么以5为旋转节点,进行左旋,变成LL双红的情况,如图3

3、15节点和5节点变为LL双红的情况,先变色,将15节点变为黑色,将19节点变为红色,如图4

4、最后以19为旋转节点进行右旋如图5,该红黑树又重新平衡。

  相关解决方案