红黑树是什么?

前言

之前就有被问到过:

  • 数据库的底层数据结构是什么? 答: b+树。
  • 你有了解过红黑树吗?
  • 红黑树的特性知道吗?
  • 为什么要用b+树而不用红黑树呢?

这一系列问题如果没事先准备过感觉都挺难回答上的,这里主要从红黑树开始记吧,再慢慢推向2-3树,b树,b+树等。

为什么要用红黑树

如果你有了解过二叉查找树的话你就会知道,其实红黑树的出现就是为了解决二叉查找树的缺陷的,因为二叉查找树在特定的情况下会退化成一个线性的数据结构,这里的话我们先了解一下二叉查找树(Binary Search Tree)。

二叉查找树

二叉查找树 又名 二分查找树 ,简单理解一下它的节点查询就是一个简单的二分查找,但这个查找的性能是取决于你 树的高度 的。

二叉查找树的特性

我们可以简单理解一下二叉查找树的特性:

  • 它的左子树上的所有节点的值均小于或等于它的根节点的值。
  • 它的右子树上的所有节点的值均大于或等于它根节点的值。
  • 它的左右子树也可以分为二叉查找树。

使用二叉查找树的优点跟缺陷

下面我们来看一下理想中的二叉查找树。

我们知道,二叉查找树查找一个数据,查询所需要最大的次数即为 二叉查找树的高度 ,在查询节点的时候,通过一层一层比较大小,找到对应的节点。

理想二叉查找树

最理想的状态 ,查询速度为O(logn),访问性能跟二分查找差不多。

但事实上二叉查找树是存在最差的情况的,同样的数据结构,也有可能存在近似与 线性查找 的效率,如下图:

线性二叉查找树

最坏的情况 ,查询速度为O(n),只存在一边子树的情况,这种情况需要将二叉树的全部节点遍历一遍。

所以,为什么要用红黑树,其实就是要避免 最坏的情况下会耗费近乎线性查找的耗时 的情况出现,后面我会提到,其实红黑树本质上也是一种 二叉查找树 ,它只是在二叉查找树的前提下,在节点类中新增了一个用来表示颜色的字段,并且它具有 一定的规则 使得红黑树的性能始终能够达到理想中的均衡状态( 插入、删除、查找的时间复杂度均为O(logn)

红黑树的特点

想必大家已经清楚了解了红黑树,其实就是在二叉查找树的结构里多了个用来表示颜色的字段,那么这个红和黑到底有什么用,需要来标记什么?为什么红黑树可以一直保持它的各种操作的稳定性呢?

我们先来看看红黑树的特点:

  1. 节点不是红就是黑。
  2. 根节点总是黑色的。
  3. 所有叶节点都是黑色的(红黑树的叶子节点都是空节点)。
  4. 如果节点是红色,那么他的子节点必须是黑色的。(从每个叶子到根的所有路径上不能有两个连续的红色节点)
  5. 从任意节点到其每个叶子节点的所有路径都包含相同数量的黑色节点。
我是红黑树

红黑树的插入操作

变色、左旋、右旋 是红黑树在二叉树上的扩展操作。同时红黑树的五个特性也是基于这三个操作才能遵守的。

这里如果不熟悉普通二叉查找树的操作的可以先去看看 二叉查找树的基本操作 代码,很简单的,插入删除查找什么的。

因为红黑树是 特化的二叉查找树 ,所以红黑树上的读操作是跟普通二叉查找树相同的。但是它的写入操作就不一样了,在写入时在不平衡的情况下,必须基于 变色、左旋、右旋 等操作来让红黑树继续平衡。下面我们具体看看:

变色

变色 仅仅指的是红黑树的节点变色,不涉及到别的操作,因为红黑树的节点必须是 「 红 」 或者 「 黑 」 这两个颜色,所以变色只是将当前的节点颜色进行变化,以满足它的特性。

假如新插入的节点的父节点为红色,并且存在伯伯节点,那么就需要涉及到 节点变色 操作,如图:

变色1

新插入节点存在父节点和伯伯节点,并且他们都是红色的情况下,涉及到变色。(其它旋转操作也涉及到变色,这里主要是变色)

这里的话显然当前节点和父节点都是红色,不满足特性,所以将父节点改成黑色,祖父节点改成红色。


变色2

发现祖父节点为根节点,改成黑色即完成变色操作,如图:


变色3

这里说下另外一个情况,假设变完色后 祖父节点存在父节点 ,并且 祖父节点和父节点都是红色 ,那么就得把祖父节点当作新插入的节点继续向上做平衡处理。

左旋转、右旋转

当新插入节点后已经 不满足红黑树特性 了,就需要涉及到 树的旋转 ,首先我们先看看什么样的情况下需要涉及到旋转的:

  1. 伯伯节点不存在的情况下, 新插入节点与父节点和祖父节点 同在一条斜线 下,如图:
    旋转1-1

    数字3 为新加入节点,并且存在 不平衡,不满足红黑树特性 的状态,首先左旋,然后交换父节点和祖父节点的颜色。
    旋转1-2
  1. 伯伯节点不存在的情况下, 新插入节点于父节点和祖父节点 不在同一条斜线 下,这种情况下会先将新插入的节点与父节点进行一次旋转,让它的结构成为在 插入节点,父节点,祖父节 点在同一条斜线的状态。
    2-1

    如上图,会将,数字2和数字3进行左旋:
    2-2

    然后再右旋:
    2-3

总结

插入后的修复操作是一个 「 从插入节点到根节点的回溯操作 」, 一旦涉及到的相关节点都符合了红黑树的特性,修复操作结束。

之所以会需要回溯是因为,变色或者旋转都会触发 「 树不平衡或者颜色不一致 」 的状态,这里就需要对祖父节点继续向上做 红黑树平衡调整操作。

所以新增节点,调整红黑树就是一个持续向上回溯的过程,最终使得红黑树始终保持平衡。

总结

这里主要记录一下红黑树是如何 结合变色和左右旋 进行新节点插入的,红黑树还有另外一个难点在删除也比较复杂,下次再看好了一次性也吸收不了那么多。

红黑树的实际应用有挺多的,java 里面的集合类 TreeMapTreeSet 底层数据结构就是红黑树实现的。在 Java8 中的 HashMap 也用到了红黑树。

小疑问 : 为什么大家都说是叔叔节点呢?爸爸的兄弟不应该是伯伯节点吗!?

都结合网上资料加上自己的一些理解,如果有影响到他人的地方,可以联系我:[email protected]

你可能感兴趣的:(红黑树是什么?)