7. 红黑树

2018-06-14  本文已影响0人  執著我們的執著

红黑树背景

红黑树(Red Black Tree) 也是一种自平衡二叉查找树,它是在1972年由Rudolf Bayer发明的,当时被称为平衡二叉B树(symmetric binary B-trees)。后来,在1978年被 Leo J. Guibas 和 Robert Sedgewick 修改为如今的“红黑树”。
红黑树和AVL树类似,都是在进行插入和删除操作时通过特定操作保持二叉查找树的平衡,从而获得较高的查找性能。
它虽然是复杂的,但它的最坏情况运行时间非常良好,在实践中效率高效: 它可以在O(log n)时间内做查找,插入和删除,这里的n 是树中元素的数目。

应用背景

实现一种数据结构,可以对历史版本的访问
例如:
search( ver, key):除了关键码key,还提供了版本号ver
insert( ver, key )remove( ver, key)
如果一个数据结构能够支持这种类型的需求,就称之为Presistent Structure(一致性结构,持久性)

如何实现?

  1. 蛮力实现:对每个版本独立保存一份,各个版本入口自成一个搜索结构
    Version

h = |history| : 历史版本数
时间上单次O(log h + log n),而空间复杂度难以接受(每个版本都存一份),累计O(h*n)时间/空间
So,如何控制这里的复杂度呢?将复杂度控制在O(n+h*log n)内?

  1. 需要利用相邻版本之间的关联性实现
    解决方案:
    O(1)重构

红黑树结构
定义:
由红,黑两类节点组成的BST (其中统一增设外部节点NULL,使整棵树成为二叉树),满足一下4个条件

注:
(2)中外部节点为黑色的NULL节点,增设的!!
(4)中的特性,确保所有外部节点到根节点的路径中,没有一条路径会比其他路径长出2倍。因而,红黑树是相对平衡的二叉树!!!

红黑树图示
RB-Tree

通过上图,对红黑树进行提升变换lifting):即将所有的红色节点提升至与其父亲相同的高度;
红黑树提升变换的意义:所有底层节点都变成同一水平高度,平齐;
从上面lifting变换后的角度来看,红黑树就是4B-Tree(即(2,4)树);

红黑树 == (2,4)B 树


红黑树常用接口定义
1. Search()       // 沿用 BST 的搜索算法
2. BinNode * insert(const T &e)      // 插入算法(重写,动态)
3. bool remove(const T &e)           // 删除算法(重写,动态)

4. 动态操作后若不满足红黑树定义的结构,进行拓扑结构修正操作,使之满足
4.1 void SolveDoubleRed(BinNode * x)     // 双红修正
4.2 void SolveDoubleBlack(BinNode * x)   // 双黑修正

5. 动态维护节点的高度
更新节点 x 的高度,注:只统计黑节点的高度!!!(重写)
int updateHeight(BinNode * x)   更新节点 x 的高度,注:只统计黑节点的高度!!!

1. RB-Tree 更新节点高度,只计黑节点高度
#define NodeHeight(p) ((p) ? p->height : 0)       //节点高度,约定空树的节点高度为 0

int updateHeight(BinNode * x)
{
    x->height = max(NodeHeight(x->lchild), NodeHeight(x->rchild));

    if (IsBlack(x))
    {
        x->height ++ ;      // 只计黑色节点
    }
    
    return x->height;
}
2. RB-Tree 插入节点操作

RB-Tree == 4阶B-Tree,借助B-Tree的模型,能更好的了解红黑树相关算法的原理&过程

借助B-Tree理解红黑树的动态变换
插入算法流程:
双红缺陷:double-red # p->color == x->color = red

若出现双红缺陷,如何修复呢?SolveDoubleRed()

注:因为出现双红,p是红色节点,那么g一定不为红,有g != NULL && g->color = B

u节点即x的叔父,其中 u 的颜色不定

插入算法框架实现

BinNode * insert(const T &e)
{
    1. 确认目标节点不存在(留意对 _hot 的设置)
    BinNode * x = search(e);
    if (NULL != x)
        return x;

    2. 创建红节点 x,以search()后的 _hot 为父,黑深度-1
    x = new BinNode(e, _hot, NULL, NULL, -1);
    _size ++;

    3. 如有必要,需要做双红缺陷修正
    SolveDoubleRed(x);

    4. 返回插入的节点
    return x ? x : _hot->parent;
}//无论原树中是否存在e,返回时总有 x->data = e
详细讨论双红缺陷的两种情况以及解决方法
  1. RR-1 : 插入节点x的叔父节点u的颜色为黑色u->color == B

  2. RR-2 :插入节点x的叔父节点u的颜色为红色u->color == R

3. 红黑树的删除节点算法

相对于插入,删除操作更加复杂
理解方面:通过lifting变换,转换成B-Tree来理解

上一篇 下一篇

猜你喜欢

热点阅读