什么是红黑树
红黑树二叉搜索树的一种,那么有了二叉搜索树了为什么还要红黑树呢?
因为再二叉搜索树上面的操作如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;
红黑树实例图如下:
上图中, (a)是把所有的NIL结点都画出来, (b)是将(a)的所有NIL结点只用一个T.nil结点来代替(注意:根结点的parent是指向T.nil结点的), (c)则是省略掉NIL结点画法。
旋转
由于INSERT和DELETE这两个操作对树做了修改,可能会违反红黑树的性质,为了维护这些性质,必须要改变树中某些结点的颜色以及指针结构.
指针结构的修改是通过旋转(rotation)来完成的, 这是一种能保持二叉搜索树性质的搜索树局部操作。
左旋转
这里假设node.right != T.nil 且根结点的父亲结点为T.nil
如图:
伪代码如下:
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);
}