Java技术栈

www.javastack.cn

关注优质文章

为什么要有红黑树

想必大家对二叉树搜索树都不陌生,首先看一下二叉搜索树的定义:

二叉搜索树(Binary Search Tree),或者是一棵空树,或者是具有下列性质的二叉树:若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;它的左、右子树也分别为二叉排序树。

从理论上来说,二叉搜索树的查询、插入和删除一个节点的时间复杂度均为O(log(n)),已经完全可以满足我们的要求了,那么为什么还要有红黑树呢?

我们来看一个例子,向二叉搜索树中依次插入(1,2,3,4,5,6),插入之后是这样的

退化成链表的二叉搜索树

可以看到,在这种情况下,二叉搜索树退化成了链表!!!这时候查询、插入和删除一个元素的时候,时间复杂度变成了O(n),显然这是不能接受的。出现这种情况情况的原因是二叉搜索树没有自平衡的机制,所以就有了平衡二叉树的概念。

平衡二叉树(Balanced Binary Tree)具有以下性质:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。

还是刚刚的例子,假如我们用平衡二叉树来实现的话,插入完元素后应该是下面这样的(不唯一)

自平衡的二叉树

平衡二叉树保证了在最差的情况下,二叉树依然能够保持绝对的平衡,即左右两个子树的高度差的绝对值不超过1。

但是这又会带来一个问题,那就是平衡二叉树的定义过于严格,导致每次插入或者删除一个元素之后,都要去维护二叉树整体的平衡,这样产生额外的代价又太大了。

二叉搜索树可能退化成链表,而平衡二叉树维护平衡的代价开销又太大了,那怎么办呢?这就要谈到“中庸之道”的智慧了。说白了就是把平衡的定义适当放宽,不那么严格,这样二叉树既不会退化成链表,维护平衡的开销也可以接受。

没错,这就是我们要谈的红黑树了。首先看一下红黑树的定义:

红黑树是一种含有红黑结点并能自平衡的二叉查找树。它必须除了满足二叉搜索树的性质外,还要满足下面的性质:

性质1:每个节点要么是黑色,要么是红色。

性质2:根节点是黑色。

性质3:每个叶子节点(NIL)是黑色。

性质4:每个红色结点的两个子结点一定都是黑色。

性质5:任意一结点到每个叶子结点的路径都包含数量相同的黑结点。

这就是红黑树的五条性质。我相信很多人都看到过,能背下来的也不在少数,但是真正理解为什么要这样定义的恐怕就不多了。下面就从2-3树的角度来谈谈红黑树的定义。

从2-3树来看红黑树

一般我们接触最多的是二叉树,也就是一个父节点最多有两个子节点。2-3树与二叉树的不同之处在于,一个父节点可以有两个子节点,也可以有三个子节点,并且其也满足类似二叉搜索树的性质。

还有最重要的,2-3树的所有叶子节点都在同一层,且最后一层不能有空节点,类似于满二叉树。

我们依次插入10,9,8,7,6,5,4,3,2,1来看一下2-3数是如何进行自平衡的。

2-3树在插入元素之前首先要进行一次未命中的查找,然后将元素插入叶子节点中,之后再进行平衡操作,下面具体说明。

首先插入10,如下图

2-3树中插入10

然后插入9,9小于10,2-3树在插入时要将9融入10这个叶子节点中(当然也是根节点),融合完成后如下:

2-3树中插入9

这是一个3节点,不用执行平衡操作。2-3树中把有两个元素,三个子节点的节点称为3节点,把有一个元素,两个子节点的的节点称为2节点。

接着插入8,插入8的时候同样要先融入叶子节点中,如下图左侧所示

2-3树中插入8

8融入叶子节点后,该结点便拥有了3个元素,不满足2-3树的定义,这时就要把3节点进行分裂,即把左侧和右侧的元素分裂为2节点,而中间的元素抽出,继续融入其父节点,在这里便成为了一个根节点。

继续插入7,如下

2-3树中插入7

插入后,各个节点都满足2-3树的定义,不需要进行平衡操作。

接着插入6,还是首先找到叶子节点,然后将其融入,如下图左侧所示

2-3树中插入6

插入后6、7、8三个元素所在的叶子节点不再满足2-3树的定义,需要进行分裂,即抽出元素7融入父节点,6和8分裂为7的左右子节点。

接着插入5,如下图所示,同样不需要进行平衡操作

2-3树中插入5

接着插入4,还是首先找到叶子节点,然后将其融入,如下图左侧所示

2-3树中插入4

插入后4、5、6三个元素所在的叶子节点不再满足2-3树的定义,需要进行分裂,即抽出元素5融入父节点,4和6分裂为5的左右子节点。

5融入父节点后,该结点便有了5、7、9三个元素,因而需要继续分裂,元素7成为新的根节点,5和9成为7的左右子节点。

接着插入3,3融入4所在的叶子节点中,不需要进行平衡操作

2-3树中插入3

接着插入2,还是首先找到叶子节点,然后将其融入,如下图左侧所示

2-3树中插入2

插入后2、3、4三个元素所在的叶子节点不再满足2-3树的定义,需要进行分裂,即抽出元素3融入父节点,2和4分裂为3的左右子节点,3融入5所在的父节点中。

最后插入2,同样先找到叶子节点,然后将其融入,如下图所示

2-3树中插入1

至此,我们便完成了在2-3树中依次插入10,9,8,7,6,5,4,3,2,1,并且2-3树始终维护着平衡。怎么样,是不是很神奇。

再看红黑树

那么红黑树与2-3树有什么关系呢?现在我们对2-3树进行改造,改造成一个二叉树。怎么改造呢?

对于2节点,保持不变;对于3节点,我们首先将3节点中左侧的元素标记为红色,如下图2所示。

2-3树到红黑树的改造

然后我们将其改造成图3的形式;再将3节点的位于中间的子节点的父节点设置为父节点中那个红色的节点,如图4的所示;最后我们将图4的形式改为二叉树的样子,如图5所示。

图5是不是很熟悉,没错,这就是我们常常提到的大名鼎鼎的红黑树了。

下面我们回过头再看下红黑树的五条性质。

从2-3树看红黑树

性质1:每个节点要么是黑色,要么是红色。

2-3树中存在2节点和3节点,3节点中左侧的元素便是红色节点,而其他的节点便是黑色节点。

性质2:根节点是黑色。

在2-3树中,根节点只能是2节点或者3节点,2节点与3节点在红黑树中的等价形式,如下图所示

2节点与3节点在红黑树中的等价形式

显然,无论是哪种情况,根节点都是黑色的。

性质3:每个叶子节点(NIL)是黑色。

这里的叶子节点不是指左右子树为空的那个叶子节点,而是指节点不存在子节点或者为空节点被称作叶子节点。在性质2中我们讨论的根节点是黑色的都是讨论根节点不为空的情况,若红黑树是一个空树,那么根节点自然也是空的叶子节点,这时候叶子节点便必然是黑色的。

性质4:每个红色结点的两个子结点一定都是黑色。

还是从2-3树的角度来理解,红色节点对应2-3树中3节点左侧的元素,那么它的子节点要么是2节点,要么是3节点。无论是2节点还是3节点对应的节点颜色都是黑色的,这在性质2时已经讨论了。

性质5:任意一结点到每个叶子结点的路径都包含数量相同的黑结点。

性质5应该是红黑树最重要的一条性质了。2-3树是一颗绝对平衡的树,即2-3树中任意一个节点出发,到达叶子节点后所经过的节点数都是一样的。那么对应到红黑树呢?2-3树中2节点对应到红黑树便是一个黑色的节点,而3节点对应到红黑树是一个红色节点和一个黑色节点。所以,无论是2节点还是3节点,在红黑树中都会对应一个黑色节点。那么2-3树中的绝对平衡,在红黑树中自然就是任意一结点到每个叶子结点的路径都包含数量相同的黑结点了。

相信大家现在已经对红黑树的五条性质有了更加深刻的体会了。那么我们再看下红黑树维持平衡的三种操作,即变色、左旋、右旋怎么理解呢?

首先看一下变色,以下图为例,

红黑树的变色

在2-3树中插入节点3后,便不再满足2-3树的定义,需要进行分解,将元素2抽出作为1和3的父节点,然后2继续向上融合。

对应到红黑树中就是,首先插入节点3,在红黑树中新插入的节点默认为红色,然后不满足定义,所以需要进行分解,分解后各个节点都为2节点,所以变为黑色。而2节点需要继续向上融合,故要变成红色。

接着看一下右旋转,以下图为例,

红黑树的右旋转

插入元素1后,进行右旋转操作,首先把2节点与3节点断开连接,同时把2与2的右子树断开连接,然后把2的右子树连接至3的左子树位置,不会违背二分搜索树的性质,然后再把3连接至2的右子树位置。最后还要改变对应节点的颜色,即把2节点的颜色改为原来3节点的黑色,把3节点的颜色改为原来2节点的红色。

接着看一下

左旋转,与右旋转类似,以下图为例,

红黑树的左旋转

插入元素3后,进行左旋转操作,首先把2节点与3节点断开连接,同时把3与3的左子树断开连接,然后把3的左子树连接至2的右子树位置,不会违背二分搜索树的性质,然后再把2连接至3的左子树位置。最后还要改变对应节点的颜色,即把2节点的颜色改为原来3节点的红色,把3节点的颜色改为原来2节点的黑色。

写在最后

最后需要说的是,本文中提到的红黑树是一种特殊的红黑树——左倾红黑树,即红色节点都是父节点的左子树,其实按照红黑树的定义不必这样。

只要满足红黑树的五条性质,就是红黑树,比如完全可以实现右倾红黑树等等,希望大家不要有误解。

关注Java技术栈看更多干货

戳原文,获取更多福利!

java 树什么意思是什么意思是什么意思_什么是红黑树?看完这篇你就明白了!...相关推荐

  1. java与python难度对比_Python和Java的区别,看完这篇文章你就清楚啦

    众所周知,在数不清的编程语言中Java自诞生之日起长盛不衰,可谓是神话般的存在.随着人工智能时代的到来,Python迅速席卷全球,作为当下最热门的编程语言,因其简单实用且应用场景广泛备受青睐. 一个是 ...

  2. 【Java 数据结构 算法】宁可累死自己, 也要卷死别人 12 红黑树

    [Java 数据结构 & 算法]⚠️宁可累死自己, 也要卷死别人 12⚠️ 红黑树 概述 红黑树 红黑树的实现 Node 类 添加元素 左旋 右旋 完整代码 概述 从今天开始, 小白我将带大家 ...

  3. 程序员要怎么高效学习Java,大学生or小白的你看完这篇的你离BAT又近了一大步

    这篇文章大体上会从以下几个部分展开: 认清自己. 学习目的. 时间管理. 学习方法. 学习的步骤. 获取知识的途径 影响学习的几个因素 自己的心态. 外物的影响. 其他想说的 大学生的学习 一些感悟 ...

  4. 看完这篇你还敢说分不清 Java 类 对象 实例 变量间的区别?

    看完这篇你还敢说分不清 Java 类 对象 实例 变量间的区别? 什么是类? 什么是对象? 什么是变量? 对象和类的关系: Java中的类: Java中的对象 Java中的变量 Java 中调用 对象 ...

  5. java 8 stream_深度分析:java8的新特性lambda和stream流,看完你学会了吗?

    1. lambda表达式 1.1 什么是lambda 以java为例,可以对一个java变量赋一个值,比如int a = 1,而对于一个方法,一块代码也是赋予给一个变量的,对于这块代码,或者说被赋给变 ...

  6. 看完这篇,java遍历字符串列表

    今年互联网形式依旧严峻,再次爆发几次大规模裁员潮.我决定把这篇文章分享出来帮助那些对前途感到迷茫的朋友. 在猎头的眼里,我已不是根正苗红的程序员.何为根正苗红?计算机专业毕业,从毕业起就从事特定方向的 ...

  7. 大厂首发!我把所有Java框架整理成了PDF,看完这篇彻底明白了

    前言 时至今日, Spring在Java生态系统与就业市场上,面试出镜率之高,投产规模之广,无出其右.随着技术的发展,Spring从往日的IoC框架,已发展成Cloud Native基础设施,衍生出大 ...

  8. 全网最火Java面试题大全1000+面试题附答案详解面试官说,看完稳了

    进大厂是大部分程序员的梦想,而进大厂的门槛也是比较高的,所以这里整理了一份阿里.美团.滴滴.头条等大厂面试大全,其中概括的知识点有:Java.MyBatis.ZooKeeper.Dubbo.Elast ...

  9. 【Java学习—(13)看完这篇文章,这些继承、多态、抽象、接口就是小儿科】

    文章目录 面向对象编程 前言 继承 关于protected(继承权限)访问权限 super 关键字 super 修饰属性 super 修饰构造方法 super 修饰普通方法 final 关键字和继承的 ...

  10. java使用原始套接字技术进行数据包截获_Linux零拷贝技术,看完这篇文章就懂了...

    本文讲解 Linux 的零拷贝技术,云计算是一门很庞大的技术学科,融合了很多技术,Linux 算是比较基础的技术,所以,学好 Linux 对于云计算的学习会有比较大的帮助. 为什么需要零拷贝 传统的 ...

最新文章

  1. android studio gradle 配置
  2. DIV+CSS 网页布局之:三列布局
  3. pLSQL中文乱码问题
  4. 设置servlet或action作为欢迎页面
  5. C++ 点(.)操作符和箭头(->)操作符
  6. 对抗搜索(Adversarial Games)——Min-max搜索 Aleph-beta剪枝搜索
  7. Aspose.Cells生成Excel图表
  8. 笔记本风扇噪音太大,最简单操作风扇降噪
  9. mysql 建复合索引_mysql 建立复合索引
  10. knife4j文档-个人笔记
  11. python少儿趣味编程多少钱一节_学费昂贵的少儿编程课,为什么被追捧?
  12. 计算机实训安全教育,暑期实践 | 计算机学院举行暑期社会实践动员暨安全教育大会...
  13. 使用gomail发送邮件
  14. learn more ,study less(一):整体性学习策略
  15. 5.Flink对接Kafka入门
  16. 浅谈对网络安全的认识(非原创)
  17. 基于STM32的DHT11温湿度传感器设计
  18. 网络爬虫获取股票历史行情
  19. WaitForSingleObject 返回值为 WAIT_ABANDONED 的情况
  20. AJAX葡萄酒价位,智利葡萄酒品牌

热门文章

  1. Adobe携手微软、世纪互联在中国推多种云服务
  2. 肖文吉mysql_疯狂软件教育中心肖文吉老师_MYSQL视频教程
  3. html中文字怎么自动排版,CSS网页布局中文字排版九大技巧
  4. 百度云apkg手机文件怎么打开_无需会员 | 手机免费解压百度云压缩文件
  5. Lifelong learning with dynamically expandable networks - 使用动态可扩展网络的终身学习-不完全记录
  6. DEDECMS验证码不正确及其不显示问题
  7. 逻辑数据库设计 - 单纯的树(递归关系数据)
  8. Android知识图谱(持续更新中)
  9. python表格绘制斜线表头_Java中使用POI在Excel单元格中画斜线—XLSX格式
  10. [TimLinux] Python3 Coverity zeep/SOAP 库使用示例