本篇博客我会重点介绍对红-黑树的理解,重点介绍红-黑树的查找,这里我们将要讨论的算法称为自顶向下插入,也就是把沿着树向下查找插入点

Ⅰ、平衡树和非平衡树

  平衡树和非平衡树:当插入一组数据关键字是按照升序或者降序插入的话此时就是集中最极端的不平衡树,此时也可看做是一个链表此时对于此树的查找的时间复杂度为O(N),所以搜索不平衡树的时间复杂度介于平衡树时间复杂度O(logN)和O(N)之间时间具体取决于树的不平衡度。

Ⅱ、红-黑树规则

  接下来我们要讨论的红-黑树具有以下几个特征(红-黑树规则):

  •   1.每一个节点不是红色就是黑色
  •   2.根总是黑色的
  •   3.若节点是红色的,则它的子节点必须全是黑色的(反过来就不一定成立)
  •   4.从根到叶节点或空子节点的每一条路径必须包含相同数目的黑色节点

  第四条中的“空子节点”指的是有右子节点的节点可能接左子节点的位置,或者说有左子节点的节点可能接右子节点的位置。就是非叶节点本可能有,但是实际上没有的那个子节点(若节点只有右子节点,那么空缺的左子节点就是空子节点,反之亦然)下图中50→25→25的右子节点(空子节点)黑色高度为1,从根到6以及到75的黑色节点数都为2,这样就违背了规则4,此树到叶节点的黑色节点数相同,但是有一个空子节点的黑色节点数为1所以此树不是红-黑树。

  从根节点到叶节点路径上面的黑色节点数目称为黑色高度,所以上面的规则四的另一种陈述方法是:所有从根到叶节点路径上的黑色高度必须相同。

  为了满足红黑树规则我们可能会使用到下面两种方式来进行修正,从而使一棵树满足红黑树规则,

  1. 改变节点颜色
  2. 执行旋转操作

  为什么保证上面四个红-黑树规则就能保证红-黑树是平衡树呢?从红黑树规则分析我们可以得到若一棵树超过了两层不平衡怎这棵树则不可能满足红-黑树规则,超过两层不平衡则表明有路径的节点数比另外的路径的节点数多一个以上,多出来的要么有更多的黑色节点,若这样则违背了红-黑树规则4,要么至少有两个相邻接的红色节点,这样就违背了红色节点的子节点必须全部是黑色节点。

Ⅲ、树的旋转:

  利用树的旋转来重新排列一些节点,来平衡一棵树,旋转必须要做到两件事情:

  1. 使一些节点上升,一些节点下降,帮助树平衡
  2. 保证不破坏二叉搜索树的特征(由于搜索算法依赖此特征,不然就没意义)

  上面利用红黑树的颜色规则和节点的颜色变化只是有助于决定何时执行旋转。光改变颜色并不能让分树达到平衡,旋转才是让树平衡的关键操作,所以我们要好好理解树的旋转操作;做旋转的时候要注意做右旋顶点必须要有一个左子节点,左旋顶端必须要有右子节点,不然旋转后顶点会没有节点,接下来我们看下图的一个简单的旋转:

  上图中a→b我们可以看到该树进行了一次右旋,a图中节点37称为顶端节点50的内侧子孙(节点12是外侧子孙点),对内侧子孙而言,如果他是上移的节点的子节点,它就必须要断开和父节点的连接并且重新连接到它以前的爷爷节点之上,就好像自己成为了自己的叔叔一样。上图是旋转过程中单节点的位置变换,上面我们说的内侧子孙也可以是一棵子树,下图我们展示了假如是子树的节点的一个移动情况,大致的原理差不多是相通的。

  上图中的树进行了一次右旋,做了以下几个事情:

  •  顶端节点(50)移动到他右子节点的位置
  •  顶端节点的左子节点(25)移动到顶端的位置上
  •  以节点12为根的整棵子树都向上移动
  •  因节点为37为根的整棵子树横向移动,成为节点50的左子节点
  •  以节点75为根的整棵子树向下移动

  上面我们看到了旋转一棵树要进行怎么样的旋转,接下来我们讨论如何插入一个新节点(利用旋转和颜色规则来保持树的平衡)

Ⅳ、节点的插入

  在我们向下查找插入点的时候我们主要讨论一下三个方面

  1. 下行途中的颜色变化
  2. 向下路途上的旋转
  3. 插入节点后的旋转

  ① 下行途中的颜色变化

  首先我们讨论下行途中的颜色变化查找的方式和普通的二叉搜索树是一样的,只是在下行的途中会涉及到颜色的变化,规则如下:每当遇到一个有两个红色子节点的黑色节点时,它必须把子节点变为黑色,把父节点变为红色(除非父节点为根节点),如下图就进行了一次颜色变换

  上图中进行颜色变化没有改变从根向下过P到叶节点或空子节点路径上的黑色节点的数目因此颜色变换并没有违反规则4,这样变化的好处是在不违背规则3的情况下连接新的红色节点更容易。上面颜色变化虽然不会违背规则4但是有可能违背规则3,若上图中P节点的父节点是红色,那么变换之后就违背了规则3,那么此时就会对树进行旋转操作,

  ② 在向下的路径上的旋转

  向下路径上的旋转有两种可能性,当违背的节点是外侧的子孙节点“违背规则的节点”是指在造成红红冲突的父子节点对中的子节点,我们从50开始创建一棵新树插入以下节点:25,75,12,37,6和18在插入12和6的时候需要做颜色变换。接下来我们要插入节点3,必须对节点12以及它的子节点6和18做颜色变换,此时生成的树就是下图中的a树,此时节点25和12违背了红黑树规则3,此时需要对此树进行旋转的操作,

  这里我们设刚刚进行颜色变换的三角形的顶点为X,X的父节点为p,祖父节点为G,此时我们对上图中的a进行如下的颜色变化和旋转:

  1. 改变X的祖父节点P的颜色(本例中旋转后还必须要改变根的颜色)
  2. 改变X父节点P的颜色
  3. 以X的祖父节点G为顶旋转,向X上升的方向旋转

  这样 就满足了红-黑树的颜色规则,树就成为了一棵平衡树,此时就能很容易地插入节点3,插入之后如上图中的b图所示。

  若插入的是内侧的子孙节点,此时我们以根为50,插入25,75,12,37,31和43.在插入12和31之前需要颜色变换,然后新插入节点28,向下查找插入点的时候,在37节点的时候需要执行颜色变换,变换后如下图的a树所示,变换后25和37都是红色需要进行旋转操作,此时颜色变换三角形的顶点X为37,P为25,G为50,执行以下四步使树从新平衡

  1. 改变G的颜色
  2. 改变X的颜色
  3. 以P为顶旋转向X上升的方向旋转,旋转结果如下图b所示
  4. 以G为顶旋转,X上升方向旋转,旋转后即可轻松插入节点28

  ③ 插入节点之后的旋转

  接下来我们讨论插入节点之后的旋转,这里我们讨论插入的节点为X,其父节点为P,祖父节点为G。1.若节点X在P的一侧与P在G的一侧相同,则X就是一个外侧的子孙节点(a,d),反之若不相同则X是一个内侧子孙节点(b,c)。下图显示了”指向“左右变化的多样性

  为恢复红黑规则所执行的操作是由X和它的亲属所决定的,节点只以三种当时排列(上面的指向变换不算在内)

  1. P是黑色的。
  2. P是红色的,X是G的一个外侧子孙节点
  3. P是红色的X是G的一个内侧的子孙节点,如下图所示

可能性1:若P是黑色的 :不需要做任何事情,由于插入的节点总是红色的。若父节点是黑色的,则 没有红色节点连接红色节点冲突也不会增加黑色节点数目,所以此时插入不需要做任何事情。

可能性2:P是红色,X是G的一个外侧子孙节点:这是插入后需要一次旋转和一些颜色变化,此时我们可以创建一个根为50的树,然后插入25,75和12。在插入12之前需要做一次颜色变换,然后向该树中插入6,得到下图中的a树,此时违背了红黑树规则,需要进行一下三个步骤的变换,

  • 改变X的祖父节点G的颜色。
  • 改变X父节点P的颜色。
  • 以X的祖父节点G为顶旋转,方向为X的上升方向,变换后的树如下图中的b,再次成为一棵红黑树

可能性3:P是红色的X是G的一个内侧的子孙节点,这时需要做两次旋转和一些颜色改变,首先我们创建一颗节点为50,25,75,12,18的树(在插入节点12之前需要一次颜色变换)插入12后的树如下图a所示,插入节点18是一个内侧子孙节点,他和他的父节点都是红色,首先第一次旋转将子孙节点变为外侧子孙节点,下图中的b->c然后就可以利用同情况1相同的旋转,颜色转换和旋转的步骤如下:

  • 改变X的祖父节点25的颜色
  • 改变X节点18的颜色
  • 用X的父节点P作为顶旋转,方向为X上升的方向
  • 再以X的祖父节点25为顶旋转,方向为X上升的方向

  接下来我们讨论上面三种情况是否包含了所有的情况:

  • 首先我们假设X有一个兄弟节点S,他是P的另外一个子节点,如果P是黑色的插入X没有问题(对应情况1),如果P是红色的那么他的两个子节点必须是黑色,所以此时不能单独存在一个黑色子节点S,否则路径上的黑色高度就不一样了,当P为红色的时候X不可能有兄弟节点。
  • 另外一种可能是P的父节点G有一个子节点U,U是P的兄弟节点和X的树节点,此时如果P是黑色的,插入X不需要进行旋转。假设P是红色的,那么U必须也是红色的,不然黑色高度就不同了,这种情况在当我们沿路径向下查找插入点的时候会变换颜色,会将P和U都变换成黑色节点,所以此时有变换回了情况1。

  因此上面讨论的三种可能性是全部可能存在的情况(在可能性2和3中X可能是右子节点或者左子节点,以及G可能是右子节点或者左子节点)

Ⅴ、红-黑树的删除

  之前我们讨论的二叉搜索树编写删除的代码比编写插入的操作的代码要难很多,红-黑树也是如此,删除节点后要恢复红黑规则变得很复杂,一般我们会用其他的方法来回避,比如说只将节点做删除的标记而不真正的删除

Ⅵ、红-黑树的效率

  和一般的二叉搜索树类似,红-黑树的查找,插入删除的时间复杂度为O(log2N),红-黑树的查找时间和普通的二叉搜索树查找的时间几乎完全一样,额外的开销只是存储空间增加了一个存储红黑颜色的空间。

  插入和删除的时间增加了一个常数因子,因为不得不在下行的路径上和插入点执行颜色变换和旋转。平均一次插入大概需要一次旋转,因此插入的时间复杂度还是O(log2N)但是比普通的二叉搜索树要慢(不用做颜色变换和旋转)

  大多数应用中查找的次数比插入和删除的次数多,所以应用红-黑树取代二叉搜索树不会增加太多的时间开销,红-黑树最大的优点就是对有序的数据的操作不会慢到O(N)的时间复杂度。

JAVA数据结构之红-黑树的更多相关文章

  1. Linux 内核里的数据结构:红黑树(rb-tree)

    转自:https://www.cnblogs.com/slgkaifa/p/6780299.html 作为一种数据结构.红黑树可谓不算朴素.由于各种宣传让它过于神奇,网上搜罗了一大堆的关于红黑树的文章 ...

  2. 物联网安全himqtt防火墙数据结构之红黑树源码分析

    物联网安全himqtt防火墙数据结构之红黑树源码分析 随着5G的发展,物联网安全显得特别重要,himqtt是首款完整源码的高性能MQTT物联网防火墙 - MQTT Application FireWa ...

  3. Java集合详解6:这次,从头到尾带你解读Java中的红黑树

    <Java集合详解系列>是我在完成夯实Java基础篇的系列博客后准备开始写的新系列. 这些文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查 ...

  4. 【数据结构】红黑树-Java实现

    WIKI:https://en.wikipedia.org/wiki/Red%E2%80%93black_tree 转:红黑树(五)之 Java的实现 总结的比较精炼的: http://www.cnb ...

  5. 数据结构Java版之红黑树(八)

    红黑树是一种自动平衡的二叉查找树,因为存在红黑规则,所以有效的防止了二叉树退化成了链表,且查找和删除的速度都很快,时间复杂度为log(n). 什么是红黑规则? 1.根节点必须是黑色的. 2.节点颜色要 ...

  6. 【数据结构】红黑树与跳表-(SortSet)-(TreeMap)-(TreeSet)

    SortSet 有序的Set,其实在Java中TreeSet是SortSet的唯一实现类,内部通过TreeMap实现的:而TreeMap是通过红黑树实现的:而在Redis中是通过跳表实现的: Skip ...

  7. JDK1.8的HashMap数据结构及红黑树

    在JDK1.6,1.7中,HashMap的实现都是用基础的“拉链法”去实现,即数组+链表的形式.如下图:通过不同的hash值,来对数据进行分配存储. 关于HashMap的Entry长度,可以参考htt ...

  8. Nginx数据结构之红黑树ngx_rbtree_t

    1. 什么是红黑树? 1.1 概述 红黑树实际上是一种自平衡二叉查找树. 二叉树是什么?二叉树是每个节点最多有两个子树的树结构,每个节点都可以用于存储数据,可以由任 1 个节点访问它的左右 子树或父节 ...

  9. Java 1.8 红黑树

    红黑树 R-B Tree R-B Tree,全称 Red-Black Tree 又称为 红黑树,它是一种特殊的二叉查找树,红黑树的每个节点都有存储位表示节点的颜色,可以是红Red 或者 黑Black ...

随机推荐

  1. 牛客网多校训练第八场A All one Matrix

    题目链接:https://ac.nowcoder.com/acm/contest/888/A 题意:求出有多少个不被包含的全1子矩阵 解题思路:首先对列做处理,维护每个位置向上1的个数,然后我们从最后 ...

  2. windows7远程连接服务器出现身份验证错误,又找不到加密Oracle修正

    把以下内容复制到文本中, Windows Registry Editor Version 5.00 [HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\Cur ...

  3. 深度学习攻防对抗(JCAI-19 阿里巴巴人工智能对抗算法竞赛)

    最近在参加IJCAI-19阿里巴巴人工智能对抗算法竞赛(点击了解),初赛刚刚结束,防御第23名,目标攻击和无目标攻击出了点小问题,成绩不太好都是50多名,由于找不到队友,只好一个人跟一群大佬PK,双拳 ...

  4. 随笔记录 重置root密码 2019.8.7

    方法1:进入单用户模式 1.开机进入以下界面选择要启动的系统按e 2.找到星号行在后面添加上init=/bin/sh 3.按住Ctrl+x执行 4.进入单用户模式 5.如果passwd命令失败,可以直 ...

  5. PCA分析,及c++代码实现

    本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/fengbingchun/article/details/79235028 主成分分析(Principal Co ...

  6. lterator遍历

    iterator是一种接口机制,为各种不同的数据结构提供统一的访问机制 作用: 1.为各种数据结构,提供一个统一的.简便的访问接口: 2.使得数据结构的成员能够按某种次序排列 3.ES6创造了一种新的 ...

  7. 【JZOJ6421】匹配

    description analysis 对于普通树形\(DP\)可以设\(f[i][0/1],g[i][0/1]\)表示\([1,i]\)的线段树的最大值.方案数 \(0\)表示不选择根与某个儿子相 ...

  8. [Grt]一篇简单概括XML

    一.XML基础 XML主要用途(我认为就这三点): XML 把数据从 HTML 分离 XML 简化数据共享 XML 简化数据传输 XML 语法规则: XML 文档必须有根元素 XML 文档必须有关闭标 ...

  9. sqoop的导入|Hive|Hbase

    导入数据(集群为对象) 在Sqoop中“导入”概念指:从非大数据集群(RDBMS)向大数据集群(HDFS,HIVE,HBASE)中传输数据,叫做:导入,即使用import关键字. 1 RDBMS到HD ...

  10. lua之table|模块|包

    一.table table是   Lua的一种数据结构用来帮助我们创建不同的数据类型,如:数字.字典等. Lua table使用关联型数组,你可以用任意类型的值来作数组的索引,但这个值不能是   ni ...