十张图详解二叉树和红黑树原理

二叉查找树 Binary Search Tree(BST)

不知道大家有没有玩过一个游戏,叫“猜数字”。甲先在纸上写一个数字(如11),然后让另外一些人在1-100的范围内猜这个数字(如A猜49),若猜中,则此人输,游戏结束;若没有猜中,则由甲缩小数字范围(甲说,范围变为1-49),由下一人继续猜,直至猜出数字为止。
这也是二叉查找树的精髓,通过不断比较大小,缩小范围,最终得到要找的元素的位置。
二叉树的特性

  • 某节点的左子树节点值仅包含小于该节点值
  • 某节点的右子树节点值仅包含大于该节点值
  • 每个左右子树也必须是二叉查找树
    图解

但是,如果是这样的呢?
在这里插入图片描述
也完全符合二叉树的特性,但是这样的树,你想找“3”,就十分费劲。站在程序的角度,就要查询很多次,效率十分低下。这时,红黑树就腾空出世了。

红黑树 Red-Black Tree(RBT)

红黑树是一种含有红黑结点并能自平衡的二叉查找树。
红黑树的特性:

  • 每个节点要么是黑色,要么是红色
  • 根节点是黑色
  • 每个叶子节点(NIL)是黑色(java中为null)
  • 每个红色结点的两个子结点一定都是黑色(即不能有两个相连的红)
  • 任意一结点到每个叶子结点的路径都包含数量相同的黑结点(黑色完美平衡,但不是完美平衡)。推论:如果一个结点存在黑子结点,那么该结点肯定有两个子结点

红黑树中节点的称呼
在这里插入图片描述

红黑树实现自平衡的两大操作
变色recolor 和 rotation旋转(左旋与右旋)
具体来说,该如何操作呢?现在假设我们向红黑树中插入一个新节点 X,需要执行以下步骤:

  1. 将新插入的节点标记为红色
  2. 如果新节点 X 是根结点(root),则标记为黑色,结束
  3. 若 X 的 parent 是黑色,则结束;若 X 的 parent 是红色,且 X 不是 root,则分两种情况(3.1和3.2)
    3.1.当 uncle 是红色时

3.1.1 将 parent 和 uncle 标记为黑色
3.1.2 将 grand parent (祖父) 标记为红色
3.1.3 把 X 的祖父设置为当前节点
3.1.4 重复步骤 2、3

在这里插入图片描述
3.2.当 uncle 是黑色时
这时分四种情况
3.2.1 左左 (P 是 G 的左孩子,并且 X 是 P 的左孩子)
则手提P为根节点,P和G(原根节点)分别变色
在这里插入图片描述
3.2.2 左右 (P 是 G 的左孩子,并且 X 是 P 的右孩子)
则左旋:使 X 的父节点 P 被 X 取代,同时父节点 P 成为 X 的左孩子(X和P位置互换),然后再应用 左左情况
在这里插入图片描述
3.2.3 右右
在这里插入图片描述
3.2.4 右左
则右旋
在这里插入图片描述
左旋和右旋的精准表述
左旋:以某个结点作为支点(旋转结点),其右子结点变为旋转结点的父结点,右子结点的左子结点变为旋转结点的右子结点,左子结点保持不变。
在这里插入图片描述
右旋:以某个结点作为支点(旋转结点),其左子结点变为旋转结点的父结点,左子结点的右子结点变为旋转结点的左子结点,右子结点保持不变。
在这里插入图片描述
旋转操作是局部的。旋转能保持红黑树平衡:当一边子树的结点少了,那么向另外一边子树“借”一些结点;当一边子树的结点多了,那么向另外一边子树“租”一些结点。

红黑树查找

红黑树的查找跟二叉平衡树的查找无异。那么二叉树的查找是怎样的呢?与“猜数字”游戏的从查找无异!
二叉树查找流程
红黑树总保持黑色完美平衡,它的查找最坏时间复杂度为 O(2lgN)

红黑树删除

二叉树删除结点 找替代结点有3种情景:
情景1:若删除结点无子结点,直接删除
情景2:若删除结点只有一个子结点,用子结点替换删除结点
情景3:若删除结点有两个子结点,用后继结点(大于删除结点的最小结点)替换删除结点
比如我要删除P,则后继结点便为R,前继节点为M
(比如我要删除P,则后继结点便为R,前继节点为M)

删除结点被替代后,在不考虑结点的键值的情况下,对于树来说,可以认为删除的是替代结点!
在这里插入图片描述
(删除P相当于删除Q)

现在我们再来回顾一下删除结点时的3种情况:
情景1,删除结点无子结点,直接删除(这时删除的结点在树末)
情景2,删除结点只有一个子结点,用子结点替换删除结点(即其实相当于删除了子结点,而这个子节点在树末)
情景3,删除结点有两个子结点,用后继结点替换删除结点(相当于删除了后继结点,而后继结点在树末)
发现了吗?无论那种情景,最终删除的结点,都在树末。
所以,我们把重点,放在这个树末结点(也即是替代结点)!(因为整棵树都平衡了之后,要做的就只是替换值和删除了)
在这里插入图片描述
好,不BB了,直接来看看具体该如何删除一个结点吧。

删除情景1:替代结点是红色结点

用替代结点代替删除结点,颜色变为删除结点的颜色。(由于替代结点是红色,删除了并不会影响红黑树的平衡,所以此情况无须做自平衡处理)

删除情景2:替换结点是黑结点

当替换结点为黑色时,我们就需要做自平衡处理了。
记住,对于替换结点,先自平衡,后替换(删除)
还记得红黑树是如何自平衡的吗,对的,根据不同的情况进行左旋和右旋操作;删除结点时的自平衡,与此类似。

删除情景2.1:替换结点是其父结点的左子结点
删除情景2.1.1:替换结点的兄弟结点是红结点

在这里插入图片描述
(R是替换结点,是父结点的左子结点,其兄弟结点是红结点)
处理:
将S设为黑色
将P设为红色
对P进行左旋,得到删除情景2.1.2.3(再接删除情景2.1.2.3处理)
在这里插入图片描述

删除情景2.1.2:替换结点的兄弟结点是黑结点

当兄弟结点为黑时,其父结点和子结点的具体颜色无法确定,遂需要考虑多种情况。

删除情景2.1.2.1:替换结点的兄弟结点的右子结点是红结点,左子结点任意颜色
在这里插入图片描述
即将删除左子树的一个黑色结点 (即R),显然左子树的黑色结点少了1个,而右子树有红色结点,遂向右子树“借”个红结点来补充黑结点。(这里和红黑树的特性“任意一结点到每个叶子结点的路径都包含数量相同的黑结点”有关,此特性的推论为“如果一个结点存在黑子结点,那么该结点肯定有两个子结点”)
处理:
将S的颜色设为P的颜色
将P设为黑色
将SR设为黑色
对P进行左旋
在这里插入图片描述
(R是准备删除的)
删除情景2.1.2.2:替换结点的兄弟结点的右子结点为黑结点,左子结点为红结点
在这里插入图片描述
兄弟结点所在的子树有红结点,可向兄弟子树借红结点
处理:
将S设为红色
将SL设为黑色
对S进行右旋,得到情景2.1.2.1
接情景2.1.2.1的处理
在这里插入图片描述
删除情景2.1.2.3:替换结点的兄弟结点的子结点都为黑结点
在这里插入图片描述
此时兄弟子树都没红结点可“借”,兄弟帮忙不了,找父母呗。这种情景我们把兄弟结点设为红色(因为黑色R即将删除),再把父结点当作替代结点,自底向上处理,去找父结点的兄弟结点“借”。
处理:
将S设为红色
把P作为新的替换结点
重新进行删除结点情景处理
在这里插入图片描述

删除情景2.2:替换结点是其父结点的右子结点

删除情景2.1的镜像情景

删除情景2.2.1:替换结点的兄弟结点是红结点

处理:
将S设为黑色
将P设为红色
对P进行右旋,得到情景2.2.2.3
进行情景2.2.2.3的处理
在这里插入图片描述

删除情景2.2.2:替换结点的兄弟结点是黑结点
删除情景2.2.2.1:替换结点的兄弟结点的左子结点是红结点,右子结点任意颜色

处理:
将S的颜色设为P的颜色
将P设为黑色
将SL设为黑色
对P进行右旋
在这里插入图片描述

删除情景2.2.2.2:替换结点的兄弟结点的左子结点为黑结点,右子结点为红结点

处理:
将S设为红色
将SR设为黑色
对S进行左旋,得到情景2.2.2.1
进行情景2.2.2.1的处理
在这里插入图片描述

删除情景2.2.2.3:替换结点的兄弟结点的子结点都为黑结点

处理:
将S设为红色
把P作为新的替换结点
重新进行删除结点情景处理
在这里插入图片描述

问题

  • jdk 1.8 HashMap 中有使用到红黑树,你知道触发条件是什么吗?有读过源码是如何 put 和 remove 的吗?
  • 这里讲的是红黑树的 insert,delete 又是什么规则呢?
  • 哪些场景可以应用红黑树?
  • 你了解各种树的时间复杂度吗?

来源:https://www.jianshu.com/p/104fa73c81b3

猜你喜欢

转载自blog.csdn.net/sinat_33404263/article/details/104820999