红黑树

来源:互联网 发布:杨振宁 李政道 知乎 编辑:程序博客网 时间:2024/06/15 21:48

1. 性质

  1. 每个结点或是红色的,或设黑色的。
  2. 根结点是黑色的。
  3. 每个叶子结点(NIL)是黑色的。
  4. 如果一个结点是红色的,则它的两个子结点都是黑色的。
  5. 对每个结点,从该结点到其所有后代叶结点的简单路径上,均包含相同数目的黑色结点。

其核心思想是,对于任意一个子树(当然,整棵树本身也是它自己的一个子树), 从子树的根结点到每个叶结点的简单路径上:

  • 黑色结点数目相同。
  • 不能有连续的红色结点(可以有连续的黑色结点)。

所以,最长路径是最短路径的2倍。比如,对于一个子树,从它的根到所有叶子的每条简单路径上,黑色结点数目是N+1(N个非NIL加上一个NIL),那么其中最多嵌入N个红色结点(每个非NIL黑色结点后都嵌入一个红色结点),否则,就会出现连续的红色结点。也就是说,最短路径长度为N+1(全是黑色结点),最长路径长度为2N+1(每个非NIL黑色结点后都有一个红色结点),是2倍的关系。这就是红黑树要达到的目标:接近平衡,而不会出现有的子树过高,有的子树过低,最多2倍

需要注意一点:NIL是黑色的,也就是说,出现黑结点的地方,可能是NIL,在编程的时候需要小心。

2. 左旋转和右旋转

这里写图片描述

旋转的特点:保持二叉查找树的性质(按中根遍历,结点是有序的)。如何记忆:
x为当前结点进行左旋转x变成(它的右孩子的)左孩子(同时,保持二叉查找树的性质)。
x为当前结点进行右旋转x变成(它的左孩子的)右孩子(同时,保持二叉查找树的性质)。

3. 插入操作

插入操作的步骤是这样的:

  • 按二叉查找树的算法,将目标结点插入;
  • 把目标结点设置为当前结点,并把它着红色;
  • 这时可能违背红黑树的性质,所以,进行修正;

其难点就在于如何修正。为了理解修正算法,我们先看看这时可能违背红黑树的哪些性质:

  • 性质1:不违背;
  • 性质2:若当前结点就是根结点(插入之前,是一棵空树),则违背;否则,不违背;
  • 性质3:不违背;
  • 性质4:若当前结点的父亲是红色,则违背;否则,不违背;
  • 性质5:不违背,因为当前结点被着红色,所以,被插路径上的黑色结点数目不变;

也就是说,结点插入后,可能违背性质2和性质4,有一下几种情形:

  1. 违背性质2:当前结点为根结点(插入之前,是一棵空树)。这个容易修正,直接改着黑色就万事大吉。
  2. 不违背性质2,也不违背性质4:不违背任何性质,已经是一棵合法的红黑树,无须修正。
  3. 不违背性质2,但是违背性质4:需要修正。

下面就着重看第3种情形:违背性质4。也就是说,当前结点和它的父亲都是红色。这一共有以下3类6种(1-a, 1-b, 2-a, 2-b, 3-a, 3-b)情况:

这里写图片描述

3.1 类别1

类别1的特点是:父亲和叔叔都是红色的。注意,这种情况下,祖父结点一定存在,因为红结点一定有黑色的父亲结点。重申一下,我们违背的只有性质4:存在连续的红色结点。为了解决这个问题:

  • 我们把父亲和叔叔都改为黑色,祖父改为红色(这样做可以保证:被影响的径上的黑色结点数目不变——一增一减,不至于违背性质5)。
  • 把祖父设置为当前结点。

这里写图片描述

由于当前结点刚刚由黑色变为红色,可以把它看作新插入的结点,重头进行修正过程。若它一直违背类别1,则一直重复上面的操作,直到下面任意一种情况出现:

  1. 当前结点是根结点(违背性质2)。这时,直接把它改着黑色即可。因为,把根结点由红色改着黑色相当于:在每条由根到叶的路径上,黑结点数目都增加1,保持相等。修正结束。
  2. 当前结点的父亲是黑色的(不违背性质2,也不违背性质4)。这时已经是一棵合法的红黑树了,修正结束。
  3. 演变为类别2或者类别3(不违背性质2,但违背性质4)。

3.2 类别2

新结点插入后,可能处于类别2;也可能由类别1演变为类别2。类别2有2-a和2-b两种,但它们是对称的,所以只研究一种即可,我们单看2-a。它可以通过下图所示的步骤修正为合法红黑树。

这里写图片描述

为什么说,经过上面的修正,红黑树就合法了呢?

  1. 没有改变二叉查找树的性质(旋转操作不会改变这一性质);
  2. 由于新的子树的根(即图3中的结点4)为黑色,所以,不管x颜色如何,都不会出现连续的红色结点;
  3. 被影响的各条路径上,黑色结点数目保持不变。见下表(被影响的子路径的黑结点数目不变,那么它所在的整条路径的黑结点数目也不会变):
被影响的子路径 修正前(图1)黑色结点数 修正后(图3)黑色结点数 …->x->p->… 1 1 …->x->q->… 1 1 …->x->r->… 1 1 …->x->9->… 2 2

综上所述:对于类别2,经这一步,就可以变为合法红黑树。

3.3 类别3

新结点插入后,可能处于类别3;也可能由类别1演变为类别3。类别3有3-a和3-b两种,但它们是对称的,所以只研究一种即可,我们单看3-a。它可以通过下图所示的步骤,修正为类别2(然后由类别2修正为合法的红黑树)。

这里写图片描述

需要注意的是,由类别3修正为类别2的过程中:

  1. 没有改变二叉查找树的性质(旋转操作不会改变这一性质);
  2. 被影响的路径上,黑色结点数目保持不变。见下表(被影响的子路径的黑结点数目不变,那么它所在的整条路径的黑结点数目也不会变):
被影响的子路径 修正前(图1)黑色结点数 修正后(图2)黑色结点数 …->x->p->… 1 1 …->x->q->… 1 1 …->x->r->… 1 1 …->x->8->… 2 2

3.4 插入操作总结

可见,插入操作的修正过程:

  • 若插入后处于类别1:最坏情况下,从插入位置(叶子)直到根,一直重复出现类别1,修正次数为树的高度。好一点的情况是,在到达根之前变为合法红黑树,或者演化为类别2或类别3;
  • 若插入后处于类别2(或由1演化为类别2):经过一次修正,变为合法红黑树;
  • 若插入后处于类别3(或由1演化为类别3):经一次修正演变为类别2,再由一次修正变为合法红黑树;

也就是说,最多改色次数为树的高度;最多旋转次数是2