数据结构与算法_25 _ 红黑树(上):为何工程中都用红黑树这种二叉树

上两节,咱们依次讲了树、二叉树、二叉查找树。二叉查找树是最经常使用的一种二叉树,它支持快速插入、删除、查找操做,各个操做的时间复杂度跟树的高度成正比,理想状况下,时间复杂度是O(logn)。算法

不过,二叉查找树在频繁的动态更新过程当中,可能会出现树的高度远大于log2n的状况,从而致使各个操做的效率降低。极端状况下,二叉树会退化为链表,时间复杂度会退化到O(n)。我上一节说了,要解决这个复杂度退化的问题,咱们须要设计一种平衡二叉查找树,也就是今天要讲的这种数据结构。数据结构

不少书籍里,但凡讲到平衡二叉查找树,就会拿红黑树做为例子。不只如此,若是你有必定的开发经验,你会发现,在工程中,不少用到平衡二叉查找树的地方都会用红黑树。你有没有想过,为何工程中都喜欢用红黑树,而不是其余平衡二叉查找树呢?数据结构和算法

带着这个问题,让咱们一块儿来学习今天的内容吧!性能

什么是“平衡二叉查找树”?

平衡二叉树的严格定义是这样的:二叉树中任意一个节点的左右子树的高度相差不能大于1。从这个定义来看,上一节咱们讲的彻底二叉树、满二叉树其实都是平衡二叉树,可是非彻底二叉树也有多是平衡二叉树。学习

平衡二叉查找树不只知足上面平衡二叉树的定义,还知足二叉查找树的特色。最早被发明的平衡二叉查找树是AVL树,它严格符合我刚讲到的平衡二叉查找树的定义,即任何节点的左右子树高度相差不超过1,是一种高度平衡的二叉查找树。spa

可是不少平衡二叉查找树其实并无严格符合上面的定义(树中任意一个节点的左右子树的高度相差不能大于1),好比咱们下面要讲的红黑树,它从根节点到各个叶子节点的最长路径,有可能会比最短路径大一倍。设计

咱们学习数据结构和算法是为了应用到实际的开发中的,因此,我以为没必去死抠定义。对于平衡二叉查找树这个概念,我以为咱们要从这个数据结构的由来,去理解“平衡”的意思。blog

发明平衡二叉查找树这类数据结构的初衷是,解决普通二叉查找树在频繁的插入、删除等动态更新的状况下,出现时间复杂度退化的问题。ip

因此,平衡二叉查找树中“平衡”的意思,其实就是让整棵树左右看起来比较“对称”、比较“平衡”,不要出现左子树很高、右子树很矮的状况。这样就能让整棵树的高度相对来讲低一些,相应的插入、删除、查找等操做的效率高一些。开发

因此,若是咱们如今设计一个新的平衡二叉查找树,只要树的高度不比log2n大不少(好比树的高度仍然是对数量级的),尽管它不符合咱们前面讲的严格的平衡二叉查找树的定义,但咱们仍然能够说,这是一个合格的平衡二叉查找树。

如何定义一棵“红黑树”?

平衡二叉查找树其实有不少,好比,Splay Tree(伸展树)、Treap(树堆)等,可是咱们提到平衡二叉查找树,听到的基本都是红黑树。它的出镜率甚至要高于“平衡二叉查找树”这几个字,有时候,咱们甚至默认平衡二叉查找树就是红黑树,那咱们如今就来看看这个“明星树”。

红黑树的英文是“Red-Black Tree”,简称R-B Tree。它是一种不严格的平衡二叉查找树,我前面说了,它的定义是不严格符合平衡二叉查找树的定义的。那红黑树到底是怎么定义的呢?

顾名思义,红黑树中的节点,一类被标记为黑色,一类被标记为红色。除此以外,一棵红黑树还须要知足这样几个要求:

  • 根节点是黑色的;

  • 每一个叶子节点都是黑色的空节点(NIL),也就是说,叶子节点不存储数据;

  • 任何相邻的节点都不能同时为红色,也就是说,红色节点是被黑色节点隔开的;

  • 每一个节点,从该节点到达其可达叶子节点的全部路径,都包含相同数目的黑色节点;

这里的第二点要求“叶子节点都是黑色的空节点”,稍微有些奇怪,它主要是为了简化红黑树的代码实现而设置的,下一节咱们讲红黑树的实现的时候会讲到。这节咱们暂时不考虑这一点,因此,在画图和讲解的时候,我将黑色的、空的叶子节点都省略掉了。

为了让你更好地理解上面的定义,我画了两个红黑树的图例,你能够对照着看下。

为何说红黑树是“近似平衡”的?

咱们前面也讲到,平衡二叉查找树的初衷,是为了解决二叉查找树由于动态更新致使的性能退化问题。因此,“平衡”的意思能够等价为性能不退化。“近似平衡”就等价为性能不会退化得太严重

咱们在上一节讲过,二叉查找树不少操做的性能都跟树的高度成正比。一棵极其平衡的二叉树(满二叉树或彻底二叉树)的高度大约是log2n,因此若是要证实红黑树是近似平衡的,咱们只须要分析,红黑树的高度是否比较稳定地趋近log2n就行了。

红黑树的高度不是很好分析,我带你一步一步来推导。

首先,咱们来看,若是咱们将红色节点从红黑树中去掉,那单纯包含黑色节点的红黑树的高度是多少呢?

红色节点删除以后,有些节点就没有父节点了,它们会直接拿这些节点的祖父节点(父节点的父节点)做为父节点。因此,以前的二叉树就变成了四叉树。

前面红黑树的定义里有这么一条:从任意节点到可达的叶子节点的每一个路径包含相同数目的黑色节点。咱们从四叉树中取出某些节点,放到叶节点位置,四叉树就变成了彻底二叉树。因此,仅包含黑色节点的四叉树的高度,比包含相同节点个数的彻底二叉树的高度还要小。

上一节咱们说,彻底二叉树的高度近似log2n,这里的四叉“黑树”的高度要低于彻底二叉树,因此去掉红色节点的“黑树”的高度也不会超过log2n。

咱们如今知道只包含黑色节点的“黑树”的高度,那咱们如今把红色节点加回去,高度会变成多少呢?

从上面我画的红黑树的例子和定义看,在红黑树中,红色节点不能相邻,也就是说,有一个红色节点就要至少有一个黑色节点,将它跟其余红色节点隔开。红黑树中包含最多黑色节点的路径不会超过log2n,因此加入红色节点以后,最长路径不会超过2log2n,也就是说,红黑树的高度近似2log2n。

因此,红黑树的高度只比高度平衡的AVL树的高度(log2n)仅仅大了一倍,在性能上,降低得并很少。这样推导出来的结果不够精确,实际上红黑树的性能更好。

解答开篇

咱们刚刚提到了不少平衡二叉查找树,如今咱们就来看下,为何在工程中你们都喜欢用红黑树这种平衡二叉查找树?

咱们前面提到Treap、Splay Tree,绝大部分状况下,它们操做的效率都很高,可是也没法避免极端状况下时间复杂度的退化。尽管这种状况出现的几率不大,可是对于单次操做时间很是敏感的场景来讲,它们并不适用。

AVL树是一种高度平衡的二叉树,因此查找的效率很是高,可是,有利就有弊,AVL树为了维持这种高度的平衡,就要付出更多的代价。每次插入、删除都要作调整,就比较复杂、耗时。因此,对于有频繁的插入、删除操做的数据集合,使用AVL树的代价就有点高了。

红黑树只是作到了近似平衡,并非严格的平衡,因此在维护平衡的成本上,要比AVL树要低。

因此,红黑树的插入、删除、查找各类操做性能都比较稳定。对于工程应用来讲,要面对各类异常状况,为了支撑这种工业级的应用,咱们更倾向于这种性能稳定的平衡二叉查找树。

内容小结

不少同窗都以为红黑树很难,的确,它算是最难掌握的一种数据结构。其实红黑树最难的地方是它的实现,咱们今天尚未涉及,下一节我会专门来说。

不过呢,我认为,咱们其实不该该把学习的侧重点,放到它的实现上。那你可能要问了,关于红黑树,咱们究竟须要掌握哪些东西呢?

还记得我屡次说过的观点吗?咱们学习数据结构和算法,要学习它的由来、特性、适用的场景以及它能解决的问题。对于红黑树,也不例外。你若是能搞懂这几个问题,其实就已经足够了。

红黑树是一种平衡二叉查找树。它是为了解决普通二叉查找树在数据更新的过程当中,复杂度退化的问题而产生的。红黑树的高度近似log2n,因此它是近似平衡,插入、删除、查找操做的时间复杂度都是O(logn)。

由于红黑树是一种性能很是稳定的二叉查找树,因此,在工程中,但凡是用到动态插入、删除、查找数据的场景,均可以用到它。不过,它实现起来比较复杂,若是本身写代码实现,难度会有些高,这个时候,咱们其实更倾向用跳表来替代它。

课后思考

动态数据结构支持动态的数据插入、删除、查找操做,除了红黑树,咱们前面还学习过哪些呢?能对比一下各自的优点、劣势,以及应用场景吗?

欢迎留言和我分享,我会第一时间给你反馈。

相关文章
相关标签/搜索