红黑树介绍
红黑树是二叉排序树的一种,二叉排序树即:左节点 < 根节点 < 有节点。二叉排序树有利于查找,但当一颗二叉排序树按照大小顺序插入生成时,即每次插入的数据都比原树的所有数据大,则此时整棵都没有左节点,只有有节点,类似于链表。这时的查找效率变成了 O(n)。
为了能保证二叉树的查询效率,我们引进了平衡二叉树。平衡二叉树的特点是「左右结点相差的高度做多为一。」 这时的二叉树查找效率为 log2 (n)。
但每次插入数据时,二叉树都要进行一系列的操作,使整棵树达到平衡,效率极为低下,所以我们有引进了红黑树。
红黑树的特性为:既有了二叉排序树的特点,又有了平衡二叉树的特点,只不过查找效率比平衡二叉树低一点。
红黑树的特性
- 每个节点不是黑色就是红色。
- 根节点是黑色。
- 每个叶子节点都是黑色。(这里的叶子节点是指空节点)
- 红色节点不能作为父子节点。即如果一个节点是红色,则其子节点必须是黑色。
- 「在任意一颗子树上,从根节点到每一个叶子结点所包含的黑色节点数必须相同。」
从定义上看,红黑树可以看做一种特殊的平衡二叉树,因为每个根结点的左右两侧黑色结点必须相同。在极端的情况下,例如更节点的左边全是黑色结点,右边的结点为红色和黑色交替。假设左边黑色结点的层数为 k,则右边的结点层数为 2k -1 ,左右层数最多相差 k-1 。而平衡二叉树左右层数最多相差 1 。
查询红黑树
查询红黑树的算法跟查询二叉树的算法一样。先把查询的 key 与根节点的 key 进行判断,如果小于根节点的值,则往左子树查找;如果大于根节点的值,则往右子树查找;如果根节点的左右结点为空,则返回根节点(添加时需要此结点,如果等于根节点的值);也返回根节点。
创建红黑树
红黑树的创建是有每一个结点添加到树上的,所以我们可以从添加操作上了解二叉树的创建。
1. 红黑树添加算法的灵魂:左旋和右旋
![0217ebf97d25040f501a11273dd5d9a8.png](https://i-blog.csdnimg.cn/blog_migrate/0b5a5e8912483325c734b3ad4ae86bc5.png)
左旋是将右节点旋转成根节点,根节点旋转成左节点。
代码实现的步骤很简单:
- 获取根节点 p ,右节点 q,根节点的父节点 pre 。
- 将右节点 q 的左节点插入到 根节点 p 的有节点上(此时 q 和 p 之间的连接断裂)。
- 将根节点 p 插入到右节点 q 的左节点上。(此时 q 和 p 之间又有联系,新联系)。
- 将有节点插入到父节点 pre 上。
![4d58683be7a28b743ae6f7234f04518d.png](https://i-blog.csdnimg.cn/blog_migrate/984355ab645fea000577574b69a0c5df.png)
右旋是将左节点旋转成根节点,根节点旋转成右节点。 实现算法跟左旋一样。
2. 添加步骤:
- 创建插入结点 「q」,结点为红色
- 判断根节点是否存在,若不存在,则把插入结点变为黑色并作为根节点。
- 根据插入的数据查询红黑树,如果返回结点的 key 与插入结点的 key 相同,则更改返回结果的 value。不相同时先根据大小将插入结点插入到返回结点的左子节点或右子节点。然后开始讨论情况:
- 若返回结果为黑色结点,则直接插入,因为插入红色节点不影响红黑树的性质。
- 若返回结果为红色节点,则先判断返回节点为左节点或者是右节点。
- 若返回节点 「p」 为左节点,则先根据比较 key 值选择插入左边或者右边。
- 若插入左边,则此时 「p」,「q」,以及 「p」 的父节点 「pre」 连成一条线,则此时 「以 p 为中心」 采用右旋,且此时的颜色分别是红,红,黑(因为红色节点的父子节点不能为同一色),把颜色改为 黑,红,红。
- 若返回节点 「p」 为左节点,则先根据比较 key 值选择插入左边或者右边。
![60e9dbbdbe3080811c91f1d74fa42749.png](https://i-blog.csdnimg.cn/blog_migrate/fb5576000f51d94b0319726d4bd193c3.jpeg)
-
-
-
- 若插入左边,则此时 「p」,「q」,以及 「p」 的父节点 「pre」 为三角形,则此时 「以 q 为中心」 采用左旋,回到第一种情况。
-
-
![a0f52c1e344f77fb1cfa95c3772dc200.png](https://i-blog.csdnimg.cn/blog_migrate/9aae1ac59fef2a982762cfaaa263afec.jpeg)
-
-
- 返回 「p」 为右节点,同理,先根据比较 key 值选择插入左边或者右边。
- 若插入左边,则此时 「p」,「q」,以及 「p」 的父节点 「pre」 为三角形,则此时 「以 q 为中心」 采用右旋,使其连成一条线。
- 返回 「p」 为右节点,同理,先根据比较 key 值选择插入左边或者右边。
-
![43b71f2a49d85716ce58886fe55032bf.png](https://i-blog.csdnimg.cn/blog_migrate/15d6ec77867d29779a0abbe0f4a2f484.jpeg)
-
-
-
- 如果插入右边,则此时 「p」,「q」,以及 「p」 的父节点 「pre」 连成一条线,则此时 「以 p 为中心」 采用左旋,且此时的颜色分别是红,红,黑(因为红色节点的父子节点不能为同一色),把颜色改为 黑,红,红。
-
-
插入步骤就结束了!!!
红黑树删除结点
删除结点需要先找到该节点,找到结点后有三种情况。
- 该节点 「p」 有两个子节点。 要删除这种结点就需要把此结点替换掉。如何替换?需要从右子树中找到比它大一号的结点。找到此结点就需要通过 **以 p 结点的右节点为根节点,向左遍历左节点直到为空,将这个结点 q 的值与 p 结点的值进行交换。**此时需要删除的节点变为 「q」 节点。
![527894f730df6cb2d29537126f804877.png](https://i-blog.csdnimg.cn/blog_migrate/e550757136700ed04875faf690985bb1.jpeg)
由于遍历左节点,所以待删除的节点 「q」 只有一个子节点或者没有节点,可以由接下来分析。
- 该节点 「p」 有一个子节点。 根据节点左右两边黑子层数相同,所以 「p」 的子节点不能为黑色节点(因为只有一个子节点),所以只能为红色,又 「p」 的子节点为红色,所以 「p」 必为黑色,此时只需要交换父子节点的值,并删掉子节点就行。
![0242dd85aca9fb62403d94b77317b02b.png](https://i-blog.csdnimg.cn/blog_migrate/1414d512002481eab00f27acba7bd1b4.jpeg)
- 该节点 「p」 没有子节点。
- 若该节点 「p」 为红色,则直接删除掉。
- 若该节点 「p」 为黑色,则需分以下情况:
- 若 「p」 节点的父节点和兄弟节点都为黑色,且其兄弟节点没有子节点。则先删除 「p」,并把 「p」 的兄弟节点变为红色,「p」 的父节点的兄弟节点变为红色。
![85ca46b5c34a1cbc8027b4e631b37b01.png](https://i-blog.csdnimg.cn/blog_migrate/245f9febf842c397af1cef5f46fdde8d.jpeg)
-
-
- 若 「p」 节点的父节点和兄弟节点都为黑色,且其兄弟节点有两个子节点。则先删除 「p」,再根据 「p」 节点是左节点或右节点对应左旋或右旋。再将 「p」节点的兄弟节点的左节点或右节点相应改变颜色。
-
![d89cf0627bedd6929e6c2f19afc88683.png](https://i-blog.csdnimg.cn/blog_migrate/b80f2c8e43c9964c1a7cdfb2eaabb52b.jpeg)
-
-
- 若 「p」 节点的父节点 「pre」 和兄弟节点 「q」 都为黑色,且其兄弟节点 「q」 有一个子节点。则先删除 「p」,再根据 「q」 节点与其子节点,父节点 「pre」 所连接的形状进行选择左旋或者右旋。
-
![c6e453aa6e66b6bf2efe51c92d8c1fea.png](https://i-blog.csdnimg.cn/blog_migrate/3bd6c7e1faba36b911a141de0848c1f6.jpeg)
-
-
- 若 「p」 节点的父节点 「pre」 为红色,兄弟节点 「q」 为黑色,且其兄弟节点 「q」 没有子节点。则先删除 「p」,再把 「pre」变为黑色,「q」 变为红色。
-
![4a6b8e59ec851f65f860b728600a2ef4.png](https://i-blog.csdnimg.cn/blog_migrate/d981809763aae0df0028c70b26a02cde.jpeg)
-
-
- 若 「p」 节点的父节点 「pre」 为红色,兄弟节点 「q」 为黑色,且其兄弟节点 「q」 有一个子节点。则先删除 「p」,再把根据情况用左旋或右旋。
-
![48ed57c3a773da7fce718acc3ef05bdb.png](https://i-blog.csdnimg.cn/blog_migrate/2e859c7177cf7a3923ace0fb91053fe1.jpeg)
-
-
- 若 「p」 节点的父节点 「pre」 为红色,兄弟节点 「q」 为黑色,且其兄弟节点 「q」 有两个子节点。则先删除 「p」,再把根据情况用左旋或右旋。
-
![d2c58495cb79ff91c7da9cb97dcfba32.png](https://i-blog.csdnimg.cn/blog_migrate/9134c6286602cb97913597c8a1ede928.jpeg)
「感兴趣的朋友可以关注下公众号《慢慢编程》,慢慢在这里磕头了!」