红黑树是一种二分查找树,也算是一种自平衡的二叉树,它是在1972年由鲁道夫发明的,他称之为"对称二叉B树"。相对于AVL树的高度平衡,红黑树相对不平衡,但综合其查找、插入、删除操作的代价,仍在stl等地方广泛使用。
首先是红黑树的五条性质:
1、每个结点要么是红的,要么是黑的。
2、根结点是黑色的。
3、每个叶结点,即空结点(NIL)是黑的。
4、如果一个结点是红的,那么它的两个儿子节点都是黑的。
5、对每个结点,从该结点到其子孙结点的所有路径上包含相同数目的黑结点(黑高度相同)。
记得一点,树中每个节点包含五个域:color, key, left, right和p。如果某一个节点的指针没有可指向的节点,那么该指针将指向一个哨兵。哨兵是一个真实存在的节点,它的color被设置为black,其它域可以被设为任何值。
首先来看红黑树为什么平衡。由于性质4,每一条路径则最多有一半节点是红色(最多红黑交替排列),最少有零个红色节点,再加上性质5,拥有相同数量的黑色节点,那么最多最长是最短的高度的两倍。所以,红黑树是比较平衡的。(该证明是个人想法,详细的自己参考算法导论)
以上的个人代码
enum Color{ red, black };
struct Node{
Node * p, * left, * right;
int value;
Color color;
};
Node nilNode; //哨兵
/*初始化哨兵节点*/
void initNilNode(){
nilNode.p = NULL;
nilNode.left = NULL;
nilNode.right = NULL;
nilNode.value = -1;
nilNode.color = black; //必须
}
接下来,我们来看红黑树的最基本操作——左旋、右旋。
左旋也是类似的。
旋转的个人代码如下:
/*左旋函数(不考略x右节点为空,在调用函数前判断)*/
void leftRotate( Node ** root, Node * x ){
Node * y = (*x).right;
(*x).right = (*y).left;
(*(*y).left).p = x;
(*y).p = (*x).p;
if ( (*x).p != &nilNode ){ //原x不是根节点
if ( x == (*(*x).p).left ){
(*(*x).p).left = y;
}else{
(*(*x).p).right = y;
}
}else{
*root = y;
}
(*y).left = x;
(*x).p = y;
}
/*右旋函数(不考略x左节点为空,在调用函数前判断)*/
void rightRotate( Node ** root, Node * x ){
Node * y = (*x).left;
(*x).left = (*y).right;
(*(*y).right).p = x;
(*y).p = (*x).p;
if ( (*x).p != &nilNode ){ //原x不是根节点
if ( x == (*(*x).p).left ){
(*(*x).p).left = y;
}else{
(*(*x).p).right = y;
}
}else{
*root = y;
}
(*y).right = x;
(*x).p = y;
}
接下来就是节点插入操作
当然,基础是二分查找树的插入操作,下面是二分查找树的插入操作的个人代码:
/*插入节点函数*/
void insertRB( Node ** root, Node * insertNode ){
if ( *root == &nilNode ){
*root = insertNode;
(*insertNode).color = black;
return;
}
Node * now = *root;
Node * temp = &nilNode;
while ( now != &nilNode ){
temp = now;
if ( (*now).value < (*insertNode).value ){
now = (*now).right;
}else{
now = (*now).left;
}
}
if ( (*temp).value < (*insertNode).value ){
(*temp).right = insertNode;
}else{
(*temp).left = insertNode;
}
(*insertNode).p = temp;
(*insertNode).left = &nilNode;
(*insertNode).right = &nilNode;
(*insertNode).color = red;
fixupInsertRB( root, insertNode );
}
注意到代码最后有 “fixupInsertRB( root, insertNode );”的语句,那是因为插入节点后,若是根节点,为了性质1,直接涂黑,其他性质自动满足;不是根节点,则为了保持性质5,将其涂红。那么,如果父节点是黑色的,不需要任何操作,5条性质满足;如果父节点也是红色,则违反性质4,所以调用fixupInsertRB()函数,修复被破坏的性质。
修复的时候,情况有三种(注意,父节点是红色,那么一定还有祖父节点,否则插入前就不是红黑树)
情况一:当前节点的叔叔节点是红色的
方法是将当前节点的父节点和叔叔节点涂黑,祖父节点(一定存在且不是哨兵)涂红(保持性质5),当前节点的指针移向祖父节点,再次循环处理。
Node * p = (*insertNode).p;
//如果当前节点的父节点是祖父节点的左孩子节点,只考虑了一半,另一半对称处理
Node * temp = (*(*p).p).right;
if ( (*temp).color == red ){ //如果叔叔节点是红色
(*p).color = black;
(*temp).color = black;
(*(*p).p).color = red;
insertNode = (*p).p;
}
情况二:当前节点的叔叔是黑色的,且当前节点是右孩子
方法是对当前节点的父节点左旋(由于当前节点和父节点都是红色,左旋将不影响性质5),当前节点的指针移向左旋前的当前节点的父节点,也就是现在的左孩子节点。这样,就转化成了情况3.
if ( insertNode == (*p).right ){ //如果当前节点是父节点的右孩子节点
insertNode = p;
leftRotate( root, p );
p = (*insertNode).p;
}
情况三:当前节点的叔叔是黑色的,且当前节点是左孩子
方法是将父节点涂为黑色,祖父节点(一定存在,具体原因自己想)涂为红色,对祖父节点右旋。此时,所有性质满足。
(*p).color = black;
(*(*p).p).color = red;
rightRotate( root, (*p).p );
插入修复函数的完整代码:
/*插入修复函数(由于插入节点函数使红黑树性质缺失)*/
void fixupInsertRB( Node ** root, Node * insertNode ){
if ( insertNode == *root ){
(*insertNode).color = black;
return;
}
Node * p = (*insertNode).p;
while ( (*p).color == red ){
//父节点是红色,那么一定还有祖父节点,否则插入前就不是红黑树
if ( p == (*(*p).p).left ){ //如果当前节点的父节点是祖父节点的左孩子节点
Node * temp = (*(*p).p).right;
if ( (*temp).color == red ){ //如果叔叔节点是红色
(*p).color = black;
(*temp).color = black;
(*(*p).p).color = red;
insertNode = (*p).p;
}else{ //如果叔叔节点是黑色
if ( insertNode == (*p).right ){ //如果当前节点是父节点的右孩子节点
insertNode = p;
leftRotate( root, p );
p = (*insertNode).p;
}
(*p).color = black;
(*(*p).p).color = red;
rightRotate( root, (*p).p );
}
}else{ //如果当前节点的父节点是祖父节点的右孩子节点(与上面对称)
Node * temp = (*(*p).p).left;
if ( (*temp).color == red ){
(*p).color = black;
(*temp).color = black;
(*(*p).p).color = red;
insertNode = (*p).p;
p = (*insertNode).p;
}else{
if ( insertNode == (*p).left ){
insertNode = p;
rightRotate( root, p );
p = (*insertNode).p;
}
(*p).color = black;
(*(*p).p).color = red;
leftRotate( root, (*p).p );
}
}
p = (*insertNode).p;
}
(*(*root)).color = black;
}
下接《红黑树(二)》http://blog.youkuaiyun.com/u010169008/article/details/26057579