红黑树

什么是红黑树

红黑树二叉搜索树的一种,那么有了二叉搜索树了为什么还要红黑树呢?
因为再二叉搜索树上面的操作如SEARCH,PERDECESSOR, SUCCESSOR, MINIMUM,
MAXMUM,INSERT 和 DELETE 等, 其时间复杂度为O(h).
因此搜索树的高度较低时,这些集合操作都会执行得较快。然而,如果
树的高度较高时,这些集合操作可能并不比在链表上执行得快,红黑树是
“平衡”搜索树中的一种,可以保证在最坏情况下基本动态集合操作的时间复杂度为
O(lgn).
红黑树是特殊的二叉搜索树,它比二叉搜索树多了一点颜色.
红黑树的五个性质:
1. 每一个结点要么是红色,要么是黑色
2. 根结点必须是黑色
3. 每个叶子结点(NIL)为黑色
4. 如果一个结点是红色,那么它的两个孩子结点都为黑色
5. 对于每个结点,从该结点到其所有后代叶子结点的简单路径上,均包含相同数目的黑色结点

红黑树的结构体如下:
至少包含:color, key, left, right, parent

typedef struct _red_black_tree{
    char color; // 0 red, 1 black
    int key;
    struct _red_black_tree *left;
    struct _red_black_tree *right;
    struct _red_black_tree *parent;
}red_black_tree;

红黑树实例图如下:
红黑树_第1张图片

上图中, (a)是把所有的NIL结点都画出来, (b)是将(a)的所有NIL结点只用一个T.nil结点来代替(注意:根结点的parent是指向T.nil结点的), (c)则是省略掉NIL结点画法。

旋转

由于INSERT和DELETE这两个操作对树做了修改,可能会违反红黑树的性质,为了维护这些性质,必须要改变树中某些结点的颜色以及指针结构.

指针结构的修改是通过旋转(rotation)来完成的, 这是一种能保持二叉搜索树性质的搜索树局部操作。

左旋转

这里假设node.right != T.nil 且根结点的父亲结点为T.nil
如图:
红黑树_第2张图片

红黑树_第3张图片
伪代码如下:
LEFT-ROTATE(T, x)

y = x.right // set y
x.right = y.left // turn y's left subtree into x's right subtree
if y.left != T.nil
    y.left.parent = x
y.parent = x.parent // link x's parent to y
if x.parent == T.nil
    T.root = y
else if x == x.parent.left
    x.parent.left = y
else
    x.parent.right = y
y.left = x   // put x on y's left
x.parent = y

左旋C语言代码:

/* left rotate */
void LEFT_ROTATE(red_black_tree *root, red_black_tree *node)
{
/*
        |    left rotate   |
        y   <-----------   x
        /\                 /\
       x  z               a  y
       /\                    /\
      a  b                  b  z
*/
    red_black_tree *tmp = NULL;
    tmp = node->right;  // set y
    node->right = tmp->left; // turn y's left subtree into x's right subtree
    if (tmp->left != root->parent) // root->parent point to (T.nil)
        tmp->left->parent = node;
    tmp->parent = node->parent; // link x'parent to y
    if (node->parent == root->parent)
        root = tmp;
    else if (node == node->parent->left)
        node->parent->left = tmp;
    else
        node->parent->right = tmp;
    tmp->left = node;  // put x on y's left
    node->parent = tmp;
}

右旋转
右旋C语言代码:

/* right rotate */
void RIGHT_ROTATE(red_black_tree *root, red_black_tree *node)
{
/*
        |    right rotate  |
        y    ----------->  x
        /\                 /\
       x  z               a  y
       /\                    /\
      a  b                  b  z
*/
    red_black_tree *tmp = NULL;
    tmp = node->left;        // set x
    node->left = tmp->right; // turn x's right subtree into y's left subtree
    if (tmp->right != root->parent)
        tmp->right->parent = node;
    tmp->parent = node->parent; // link y's parent to x
    if (node->parent == root->parent)
        root = tmp;
    else if (node == node->parent->left)
        node->parent->left = tmp;
    else
        node->parent->right = tmp;
    tmp->right = node;  // put y on x's right
    node->parent = tmp;
}

插入

我们可以在 O(lgn) 时间内完成向一棵含n个结点的红黑树中插入一个新结点。
为了做到这一点,利用TREE-INSERT将结点z插入树T内,就好像T是一棵普通的
二叉搜索树一样,然后将z着为红色。为了保证红黑性质能继续保持,我们调用
一个辅助程序RB-INSERT-FIXUP来对结点重新着色并旋转。

红黑树的插入C语言代码:

/* insert node */
void RED_BLACK_INSERT(red_black_tree *root, red_black_tree *node)
{
    red_black_tree *cur = root->parent; // T.nil
    red_black_tree *prev = root;
    while ( prev != root->parent ){ // prev != T.nil
        cur = prev;
        if ( node->key < prev->key )
            prev = prev->left;
        else
            prev = prev->right;
    }
    node->parent = cur;
    if ( cur == root->parent )
        root = node;
    else if ( node->key < cur->key )
        cur->left = node;
    else
        cur->right = node;
    node->left = root->parent;
    node->right = root->parent;
    node->color = 0; // 0 red, 1 black
    RED_BLACK_INSERT_FIXUP(root, node);
}

你可能感兴趣的:(算法导论,数据结构)