红黑树 RBTree

红黑树

原文

介绍

红黑树也是二叉查找树,我们知道,二叉查找树这一数据结构并不难,而红黑树之所以难是难在它是自平衡的二叉查找树,在进行插入和删除等可能会破坏树的平衡的操作时,需要重新自处理达到平衡状态。现在在脑海想下怎么实现?是不是太多情景需要考虑了?啧啧,先别急,通过本文的学习后,你会觉得,其实也不过如此而已。好吧,我们先来看下红黑树的定义和一些基本性质。

红黑树定义和性质:
红黑树是一种含有红黑结点并能自平衡的二叉查找树。它必须满足下面性质:

  • 1.每个节点要么是黑色,要么是红色。
  • 2.根节点是黑色。
  • 3.每个叶子节点(NIL)是黑色,注意是NIL。
  • 4.每个红色结点的两个子结点一定都是黑色。
  • 5.任意一结点到每个叶子结点的路径都包含数量相同的黑结点
    ss

图1

图1就是一颗简单的红黑树。其中Nil为叶子结点,并且它是黑色的。

红黑树并不是一个完美平衡二叉查找树,从图1可以看到,根结点P的左子树显然比右子树高,但左子树和右子树的** 黑结点的层数是相等的 ** ,也即任意一个结点到到每个叶子结点的路径都包含数量相同的黑结点(性质5)。所以我们叫红黑树这种平衡为黑色完美平衡。

ss

图3

ss

图4

前面讲到红黑树能自平衡,它靠的是什么?三种操作:左旋、右旋和变色。

  1. 左旋:以某个结点作为支点(旋转结点),其右子结点变为旋转结点的父结点,右子结点的左子结点变为旋转结点的右子结点,左子结点保持不变。如图3。
  2. 右旋:以某个结点作为支点(旋转结点),其左子结点变为旋转结点的父结点,左子结点的右子结点变为旋转结点的左子结点,右子结点保持不变。如图4。
  3. 变色:结点的颜色由红变黑或由黑变红。

但要保持红黑树的性质,结点不能乱挪,还得靠变色了。怎么变?具体情景又不同变法,后面会具体讲到,现在只需要记住红黑树总是通过旋转和变色达到自平衡。

红黑树查找

因为红黑树是一颗二叉平衡树,并且查找不会破坏树的平衡,所以查找跟二叉平衡树的查找无异:

从根结点开始查找,把根结点设置为当前结点;

  1. 若当前结点为空,返回null;
  2. 若当前结点不为空,用当前结点的key跟查找key作比较;
  3. 若当前结点key等于查找key,那么该key就是查找目标,返回当前结点;
  4. 若当前结点key大于查找key,把当前结点的左子结点设置为当前结点,重复步骤2;
  5. 若当前结点key小于查找key,把当前结点的右子结点设置为当前结点,重复步骤2;

非常简单,但简单不代表它效率不好。正由于红黑树总保持黑色完美平衡,所以它的查找最坏时间复杂度为O(2lgN),也即整颗树刚好红黑相隔的时候。能有这么好的查找效率得益于红黑树自平衡的特性,而这背后的付出,红黑树的插入操作功不可没~

红黑树插入

插入操作包括两部分工作:一查找插入的位置;二插入后自平衡。查找插入的父结点很简单,跟查找操作区别不大。红黑树插入位置查找
ok,插入位置已经找到,把插入结点放到正确的位置就可以啦,但插入结点是应该是什么颜色呢?答案是红色。理由很简单,红色在父结点(如果存在)为黑色结点时,红黑树的黑色平衡没被破坏,不需要做自平衡操作。但如果插入结点是黑色,那么插入位置所在的子树黑色结点总是多1,必须做自平衡。

红黑树插入情景情景很多呢,8种插入情景!但情景1、2和3的处理很简单,而情景4.2和情景4.3只是方向反转而已,懂得了一种情景就能推出另外一种情景,所以总体来看,并不复杂,后续我们将一个一个情景来看,把它彻底搞懂。

另外,根据二叉树的性质,除了情景2,所有插入操作都是在叶子结点进行的。这点应该不难理解,因为查找插入位置时,我们就是在找子结点为空的父结点的。

插入情景1:红黑树为空树

最简单的一种情景,直接把插入结点作为根结点就行,但注意,根据红黑树性质2:根节点是黑色。还需要把插入结点设为黑色。

插入情景2:插入结点的Key已存在

插入结点的Key已存在,既然红黑树总保持平衡,在插入前红黑树已经是平衡的,那么把插入结点设置为将要替代结点的颜色,再把结点的值更新就完成插入。

插入情景3:插入结点的父结点为黑结点

由于插入的结点是红色的,并不会影响红黑树的平衡,直接插入即可,无需做自平衡。

插入情景4:插入结点的父结点为红结点

再次回想下红黑树的性质2:根结点是黑色。如果插入的父结点为红结点,那么该父结点不可能为根结点,所以插入结点总是存在祖父结点。这点很重要,因为后续的旋转操作肯定需要祖父结点的参与。

插入情景4.1:叔叔结点存在并且为红结点

从红黑树性质4可以,祖父结点肯定为黑结点,因为不可以同时存在两个相连的红结点。那么此时该插入子树的红黑层数的情况是:黑红红。显然最简单的处理方式是把其改为:红黑红。如图5和图6所示。

ss

图5

ss

图6

可以看到,我们把PP结点设为红色了,如果PP的父结点是黑色,那么无需再做任何处理;但如果PP的父结点是红色,根据性质4,此时红黑树已不平衡了,所以还需要把PP当作新的插入结点,继续做插入操作自平衡处理,直到平衡为止。

我们还可以总结出另外一个经验:红黑树的生长是自底向上的。这点不同于普通的二叉查找树,普通的二叉查找树的生长是自顶向下的。

插入情景4.2:叔叔结点不存在或为黑结点,并且插入结点的父亲结点是祖父结点的左子结点

前文说了,需要旋转操作时,肯定一边子树的结点多了或少了,需要租或借给另一边。插入显然是多的情况,那么把多的结点租给另一边子树就可以了。

  • 插入情景4.2.1:插入结点是其父结点的左子结点

将P设为黑色;将PP设为红色.对PP进行右旋。
ss

图7

> 由图11可得,左边两个红结点,右边不存在,那么一边一个刚刚好,并且因为为红色,肯定不会破坏树的平衡。 咦,可以把P设为红色,I和PP设为黑色吗?答案是可以!看过《算法:第4版》的同学可能知道,书中讲解的就是把P设为红色,I和PP设为黑色。但把P设为红色,显然又会出现情景4.1的情况,需要自底向上处理,做多了无谓的操作,既然能自己消化就不要麻烦祖辈们啦~
  • 插入情景4.2.2:插入结点是其父结点的右子结点

这种情景显然可以转换为情景4.2.1,如图7所示,不做过多说明了。
对P进行左旋
把P设置为插入结点,得到情景4.2.1
进行情景4.2.1的处理
ss

图8

插入情景4.3:叔叔结点不存在或为黑结点,并且插入结点的父亲结点是祖父结点的右子结点

  • 插入情景4.3.1:插入结点是其父结点的右子结点

将P设为黑色;将PP设为红色;对PP进行左旋
ss

图9

  • 插入情景4.3.2:插入结点是其父结点的左子结点

对P进行右旋;把P设置为插入结点,得到情景4.3.1;进行情景4.3.1的处理
ss

图10

>

posted on 2021-06-02 16:01  Ultraman_X  阅读(50)  评论(0编辑  收藏  举报

导航