23| 红黑树(上):工程中为什么常用红黑树这种二叉树?


大家好,我是爱好编程的斌斌。

能看到这篇文章,相信你对树、二叉树以及二叉查找树都有了一定的见解。二叉查找树是最常见的一种二叉树,支持快速地插入、删除、查找操作,这些操作的时间复杂度都与树的高度成正比,最好时间复杂度为O(logn).但是,在极端情况下,也就是二叉查找树经过频繁的插入、删除之后,树的高度可能远远高于log[n,2],甚至直接退化成单链表,时间复杂度为O(n).

为了解决这种极端情况下复杂度退化的问题,(尽管发生的概率很低,但是一旦发生了后果不堪设想,尤其对于单次操作时间复杂度特别敏感的系统),前辈们推出了"红黑树"这一高效的动态数据结构。

文章首先以介绍"平衡二叉树",借此引出"红黑树";然后给出它的特性,以及它拥有这种特性的原因;最后解答一下开篇的问题。后面对文章的具体内容作出了总结,已经提出了思考题,考验考验你!

一、"平衡二叉查找树"是什么?

要想了解"平衡二叉查找树",得先知道"平衡二叉树"。平衡二叉树的严格定义是这样的:二叉树中任意一个节点的左右子树的高度相差不能大于1。单从定义角度看,满二叉树和完全二叉树都是平衡二叉树。但是,平衡二叉树中也存在非完全二叉树。
在这里插入图片描述
平衡二叉查找树在满足平衡二叉树的定义上,还满足二叉查找树的特点。最先被发明的平衡二叉查找树是AVL树,它严格符合平衡二叉查找树的定义,即任何节点的左右子树高度相差不超过1,是一种高度平衡的二叉查找树

但是,并非所有的平衡二叉查找树都严格满足定义,比如我们下面要讲的红黑树,它从根节点到各个叶子节点的最长路径,有可能会比最短路径大一倍。思考一下,如果要满足定义,也就是要高度平衡,意味着在每次插入或删除之后,都要翻转节点来保证平衡,这就要消耗很多性能。而对于红黑树来说,重于应用,也就是要保证性能得好;其次,从红黑树的初衷来说,它是为了解决极端情况下二叉查找树性能退化的问题。所以,平衡二叉查找树中“平衡”的意思,其实就是让整棵树左右看起来比较“对称”、比较“平衡”,不会出现左子树很高、右子树很矮的情况。这样就能让整棵树的高度相对来说低一些,相应的插入、删除、查找等操作的效率高一些。

综上,只要树的高度不比log2n大很多(比如树的高度仍然是对数量级的),虽然它不符合我们前面讲的严格的平衡二叉查找树的定义,但我们仍然可以说,这是一个合格的平衡二叉查找树。

二、如何定义一棵"红黑树”?

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

见名知意,红黑树节点要么是红色的,要么是黑色的。除此之外,还需要满足以下特点:

  • 根节点是黑色的;
  • 每个叶子节点都是黑色的空节点(NIL),也就是说,叶子节点不存储数据;
  • 任何相邻的节点都不能同时为红色,也就是说,红色节点是被黑色节点隔开的;
  • 每个节点,从该节点到达其可达叶子节点的所有路径,都包含相同数目的黑色节点;
    你是不是觉得第二点优点奇怪,为什么叶子节点都得是黑色的呢?不存数据为啥还要浪费空间?是的,它的作用仅仅是为了简化红黑树的实现。

以下画出了两个红黑树,为了简化,叶子节点已省略:
在这里插入图片描述

三、为什么说红黑树"近似平衡"?

红黑树出现的初衷,是解决极端情况下二查查找树复杂度退化的情况。所以,这里所说的"平衡",指的是复杂度不退化;而"近似平衡"是指,复杂度退化得不是很严重。

在二叉查找树中,树相关操作的时间复杂度与树的高度成正比。而一颗极其平衡的二叉树(完全二叉树或满二叉树)的高度约为log[n,2],所以为了证明"红黑树"是"近似平衡"的,即证明红黑树的高度趋近于log[n,2]。那接下来就回到了树的高度,如何计算"红黑树"的高度呢?

首先,假设我们把红黑树的红色节点全部拿掉,没有父节点的节点,父节点变成祖宗节点,剩下的就是只包含黑色节点多叉树。然后,将多叉树变化成完全二叉树。你会发现:由红黑树去掉红色节点,而生成的多叉树,它的高度比包含相同节点数目的完全二叉树的高度好要小。而前面篇章说过,如果你还没学,可以翻翻博主的文章,完全二叉树的高度近似于log[n,2]。所以去掉红色节点的"红黑树",包含最多黑色节点的路径不会超过log[n,2].

现在我们把红色节点加上去,前面说过红黑树有这么一个特点:两个红色节点不能相邻,也就是说,树中存在一个红色节点,必定包含一个黑色节点,就假设黑色节点与红色节点个数想等,实际红色节点数量小于黑色节点数目。而前面推出的包含最多黑色节点的路径不会超过log[n,2],所以红黑树的高度不会超过2log[n,2]。
在这里插入图片描述
所以,"红黑树"的高度最多比严格平衡的二叉查找树"AVL"树的高度大一倍,退化得也不是很多。虽然这里的推到都是使用极限,但是可以推导出:红黑树是近似平衡的二叉查找树。

四、解答开篇

为什么在工程中大家都喜欢用红黑树这种平衡二叉查找树?

首先,为什么不在工程中使用极其平衡的二叉树?比如"AVL树"。"极其平衡"也就意味着,每次变化之后都要维护二叉树的平衡状态,而维护平衡这种状态需要消耗很多性能,不实用。红黑树是"近似平衡"的很大程度上避免了上面这种情况,维护成本没有"AVL树"高。

其次,红黑树的插入、查找、删除的时间复杂度都比较稳定,为logn。对于工程应用来说,要面对各种异常情况,为了支撑这种工业级的应用,更倾向于这种性能稳定的平衡二叉查找树。

五、总结

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

因为红黑树是一种性能非常稳定的二叉查找树,所以,在工程中,但凡是用到动态插入、删除、查找数据的场景,都可以用到它。不过,它实现起来比较复杂,如果自己写代码实现,难度会有些高,这个时候,我们其实更倾向用跳表来替代它。

六、课后思考

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

散列表:插入删除查找都是O(1), 是最常用的,但其缺点是不能顺序遍历以及扩容缩容的性能损耗。适用于那些不需要顺序遍历,数据更新不那么频繁的。

跳表:插入删除查找都是O(logn), 并且能顺序遍历。缺点是空间复杂度O(n)。适用于不那么在意内存空间的,其顺序遍历和区间查找非常方便。

红黑树:插入删除查找都是O(logn), 中序遍历即是顺序遍历,稳定。缺点是难以实现,去查找不方便。其实跳表更佳,但红黑树已经用于很多地方了。

  • 36
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值