1.红黑树介绍
它是在1972年由Rudolf Bayer发明的,当时被称为平衡二叉B树(symmetric binary B-trees)。后来,在1978年被 Leo J. Guibas 和 Robert Sedgewick 修改为如今的“红黑树”。
红黑树和AVL树类似,都是在进行插入和删除操作时通过特定操作保持二叉查找树的平衡,从而获得较高的查找性能。
它虽然是复杂的,但它的最坏情况运行时间也是非常良好的,并且在实践中是高效的: 它可以在O(log n)时间内做查找,插入和删除,这里的n 是树中元素的数目。
红黑树的特性:
(1)每个节点或者是黑色,或者是红色。
(2)根节点是黑色。
(3)每个叶子节点(NIL)是黑色。 [注意:这里叶子节点,是指为空(NIL或NULL)的叶子节点!]
(4)如果一个节点是红色的,则它的子节点必须是黑色的。
(5)从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点。
注意:
(01) 特性(3)中的叶子节点,是只为空(NIL或null)的节点。
(02) 特性(5),确保没有一条路径会比其他路径长出俩倍。因而,红黑树是相对是接近平衡的二叉树。
(01) 特性(3)中的叶子节点,是只为空(NIL或null)的节点。
(02) 特性(5),确保没有一条路径会比其他路径长出俩倍。因而,红黑树是相对是接近平衡的二叉树。
2.红黑树的应用
红黑树的应用比较广泛,主要是用它来存储有序的数据,它的时间复杂度是O(lgn),效率非常之高。
例如,C++ STL中的set、map,以及Linux虚拟内存的管理,都是通过红黑树去实现的。
3.左旋
/* * 对节点(x)进行左旋转
* 左旋示意图:
* px px
* / /
* x y
* / \ --(左旋)--> / \
* lx y x ry
* / \ / \
* ly ry lx ly
*/
void LeftRotate(RBTNode*& root, RBTNode* x)
{
/*设置x的右孩子节点为y*/
RBTNode* y = x->right;
x->right = y->left;
if(NULL != y->left)
y->left->parent = x;
y->parent = x->parent;
if(NULL == x->parent)
root = y;
else{
if(x = x->parent->left)
x->parent->left = y;
else
x->parent->right = y;
}
y->left = x;
x->parent = y;
}
4.右旋
/*
* 对红黑树的节点(y)进行右旋转
*
* 右旋示意图(对节点y进行左旋):
* py py
* / /
* y x
* / \ --(右旋)--> / \ #
* x ry lx y
* / \ / \ #
* lx rx rx ry
*
*/
void RightRotate(RBTNode*& root, RBTNode* y)
{
RBTNode* x = y->right;
y->left = x->right;
if(NULL != x->right)
x->right->parent = y;
if(NULL == y->parent)
root = x;
else{
if(y == y->parent->left)
y->parent->left = x;
else
y->parent->right = x;
}
x->parent = y->parent;
y->parent = x;
}
5. 插入节点
红黑树添加节点分三步完成
第一:将红黑树当作一颗二叉树,插入新节点
红黑树本身就一颗二叉树,在插入新节点后仍旧是一颗有序的二叉树
第二:将插入节点设置为红色,为什么?
回忆下红黑树的5个特性,插入新节点后有没有违背5个特性 ,应该尽量少违背这些特性,设置为红色不会违背特性5,而设置为黑色则要多违背一条特性5,因此设置为红色
第三:进行旋转和重新着色,使之重新成为一颗红黑树,插入操作可能会违背特性4(
如果一个节点是红色的,则它的子节点必须是黑色的)
//root为红黑树跟节点,node为需要插入的节点
void InsertNode(RBTNode*& root, RBTNode* node)
{
RBTNode* p=NULL;
RBTNode* cur=root;
while(NULL != cur){
p = cur;
if(node->key < cur->key)
cur = cur->left;
else
cur = cur->right;
}
node->parent = p;
if(NULL != p){
if(p->key > node->key)
p->left = node;
else
p->right = node;
}
root = node;
node->color = RED;
InsertFix(root, node);
}
void InsertFix(RBTNode*& root, RBTNode* node)
{
RBTNode* parent, gparent,uncle;
while(node->parent && node->parent->color == RED){
parent = node->parent;
gparent = parent->parent;
/*父节点是祖父节点的左孩子,则叔父节点为祖父节点的右孩子*/
if(parent == gparent->left){
uncle = gparent->right;
if(uncle->color == RED){
set_node_color_black(parent);
set_node_color_black(uncle);
set_node_color_red(gparent);
node = gparent;
continue;
}
/*叔叔节点是黑色*/
else{
/*当前接点为右孩子*/
if(node == parent->right){
LeftRotate(root, parent)
RBTNode* tmp = p;
p = node;
node =tmp;
}/*当前接点为左孩子*/
else{
set_node_color_black(parent);
set_node_color_red(gparent);
RightRotate(root, gparent);
}
}
}
/*父节点是祖父节点的右孩子*/
else{
uncle = gparent->left;
/*叔叔节点是红色*/
if(uncle->color == RED){
set_node_color_black(uncle);
set_node_color_black(parent);
set_node_color_red(gparent);
node = gparent;
continue;
}
/*叔叔节点为黑色,则祖父一定是黑色*/
else{
/*当前节点为右孩子*/
if(node == parent->right){
set_node_color_black(parent);
set_node_color_red(gparent);
LeftRotate(root, gparent)
}
/*当前节点为左孩子*/
else{
RightRotate(root, parent);
RBTNode* tmp = parent;
parent = node;
node = tmp;
}
}
}
}
/*走到这里:
*情况1:入节点即跟节点,则直接设置为黑色
*情况2:或者插入节点的父节点为黑色,原本根节点就是黑色,相当于啥也不做
*情况3:一番左右旋后把根节点设置为黑色*/
set_node_color_black(root);
}