粗略实现了红黑树,也算对自己花的时间有了点交代,暂且贴上实现代码,有心情再详细写写红黑树的一些思考。
大体是按照书上的思路来写的,RB_delete 部分并没有用 y 替换 z 再用 x 替换 y ,而是直接将 y 的值给 z 后用 x 替换 y ,省去了一些判断等操作。
简单说下感想吧,红黑树本身还是个二叉搜索树,规定了五条性质:
1. 节点为红或黑色
2. 根节点为黑色
3. 叶节点为黑色
4. 红节点的孩子为黑色(任意根到叶的路径上不存在连续的两个红色)
5. 任意根到叶的路径上黑色数目相同
这样就限制了最长路径(黑红黑红…黑)的长度不会超过最短路径(黑黑黑…黑)的两倍,这样红黑树就是一颗近似平衡的二叉搜索树了。
围绕这5个性质,最繁琐的也就是红黑树的增删工作了。增删所用到的核心操作是旋转——一种调节二叉搜索树子树高度而不破化二叉搜索树性质的操作。
左旋:使根成为左树根,使右树根成为根:左树高度+1,右树高度-1;
右旋:使根成为右树根,使左树根成为根:右树高度+1,左树高度-1。
红黑树相对比较复杂,暂时不想写什么了,谨在此贴上幸苦抄录的代码,以示自己红黑树之流不过如此。
以下为书上参考代码的 C 语言实现
/*
参考 算法导论 第13章
*/
#include<stdio.h>
#include<stdlib.h>
enum{
BLACK,
RED,
};
// 节点类型
typedef struct node {
struct node *p; // 父
struct node *left;
struct node *right;
int key;
int color;
}Node;
// 红黑树类型
typedef struct{
Node *root;
Node *nil;
}RBTree;
RBTree *getRBTree(); // 获取初始化后的树实例
void clearRBTree( RBTree *tree );
void clearRBTreeNode( Node *node, Node *nil );
void RB_insert( RBTree *tree, int key );
void RB_delete( RBTree *tree, Node *node );
Node* RB_minimum( RBTree *tree, Node *node );
Node* RB_maximum( RBTree *tree, Node *node );
void RB_print( RBTree *tree );
void RB_printNode( Node *node, Node *nil );
void RB_insert_fixup( RBTree *tree, Node *z );
void RB_delete_fixup( RBTree *tree, Node *x );
void rightRotate( RBTree *tree, Node *y );
void leftRotate( RBTree *tree, Node *x );
void RB_transplant( RBTree *tree, Node *u, Node *v );
int main(void){
RBTree *tree = getRBTree();
int keys[] = { 41, 38, 31, 12, 19, 8, 13 };
int i, n = sizeof keys / sizeof *keys;
for ( i = 0 ; i < n ; ++i ) {
RB_insert( tree, keys[i] );
}
RB_print( tree );
for( i = 0 ; i < n ; ++i ){
printf( "delete %d%c \n\t", tree->root->key, ( tree->root->color == BLACK ? 'B' : 'R' ) );
RB_delete( tree, tree->root );
RB_print( tree );
}
clearRBTree( tree );
return 0;
}
// 用 v 来替换 u 只修改与父节点的关系
void RB_transplant( RBTree *tree, Node *u, Node *v ){
if( u == tree->root ){
tree->root = v;
} else if ( u == u->p->left ) {
u->p->left = v;
} else {
u->p->right = v;
}
v->p = u->p;
}
// 右旋 将y转为右子树的根节点 保持二叉搜索树性质不变
void rightRotate( RBTree *tree, Node *y ){
Node *x = y->left;
y->left = x->right;
if( x->right != tree->nil ){
x->right->p = y;
}
x->right = y;
x->p = y->p;
if( y == tree->root ){
tree->root = x;
} else if ( y == y->p->left ) {
y->p->left = x;
} else {
y->p->right = x;
}
y->p = x;
}
// 左旋 将x转为左子树的根节点 保持二叉搜索树性值不变
void leftRotate( RBTree *tree, Node *x ){
Node *y = x->right;
x->right = y->left;
if( y->left != tree->nil ){
y->left->p = x;
}
y->left = x;
y->p = x->p;
if( x == tree->root ){
tree->root = y;
} else if ( x == x->p->left ) {
x->p->left = y;
} else {
x->p->right = y;
}
x->p = y;
}
// 红黑树删除修正 -- 找个红色补上缺失的黑色 || 从根节点统一删除一个黑度
void RB_delete_fixup( RBTree *tree, Node *x ){
while( x != tree->root && x->color == BLACK ){ // 根节点无需补黑 红节点直接刷黑
// x 为父节点的左孩子或是右孩子是对称的
if( x == x->p->left ){
Node *w = x->p->right; // 获取兄弟节点 w
// 兄弟为红色 将兄弟染上父节点颜色 将父节点染红 左旋父节点 将情况转为以下三种
// 兄弟为黑色 且兄弟的两个子节点都是黑的 则将兄弟染红 将额外的黑度加给父节点 迭代处理父节点
// 兄弟为黑色 兄弟的右孩子为黑色左孩子为红色 将兄弟染红 将兄弟的左孩子染黑 右旋兄弟 转为下一种情况
// 兄弟为黑色 兄弟的右孩子为红色 将兄弟染为父节点的颜色 将父节点染黑 将兄弟的右孩子染黑 左旋父节点 搞定
if( w->color == RED ){
w->color = BLACK; // 父节点定为黑色
w->p->color = RED;
leftRotate( tree, w->p );
w = x->p->right;
}
if ( w->left->color == BLACK && w->right->color == BLACK ) {
w->color = RED;
x = x->p;
} else if ( w->right->color == BLACK ) {
w->left->color = BLACK;
w->color = RED;
rightRotate( tree, w );
} else {
w->color = w->p->color;
w->p->color = BLACK;
w->right->color = BLACK;
leftRotate( tree, w->p );
break; // 搞定
}
} else {
Node *w = x->p->left;
if( w->color == RED ){
w->color = BLACK;
w->p->color = RED;
rightRotate( tree, w->p );
w = x->p->left;
}
if( w->left->color == BLACK && w->right->color == BLACK ){
w->color = RED;
x = x->p;
} else if ( w->left->color == BLACK ) {
w->color = RED;
w->right->color = BLACK;
leftRotate( tree, w );
} else {
w->color = w->p->color;
w->p->color = BLACK;
w->left->color = BLACK;
rightRotate( tree, w->p );
break;
}
}
}
x->color = BLACK; // 这就是红节点直接刷黑
}
// 红黑删除 先按二叉搜索树的方式删除节点 然后调用 RB_delete_fixup 修正可能的破坏行为
void RB_delete( RBTree *tree, Node *node ){
Node *y = node, *x = NULL;
int y_original_color = y->color;
if ( node->left == tree->nil ) { // 无左孩子 直接用右孩子替换 补位的节点即为右孩子
x = node->right;
} else if ( node->right == tree->nil ) { // 无右孩子 直接用左孩子替换 补位的节点即为左孩子
x = node->left;
} else { // 双孩子都有 取右子树的最小节点y补位 y一定无左孩子 故取y的右孩子x补y的位 损失的颜色为 y 的颜色
y = RB_minimum( tree, node->right );
y_original_color = y->color;
x = y->right;
// 用 y 的值替换 node 的值
node->key = y->key;
}
RB_transplant( tree, y, x ); // 替换目标节点
free( y ); // 释放被取代的节点
if( y_original_color == BLACK ){ // 失去了一个黑色 需要修正
RB_delete_fixup( tree, x );
}
}
// 红黑书的插入修正 循环条件为 z.color == RED && z.p.color == RED
void RB_insert_fixup( RBTree *tree, Node *z ){
while( z->p->color == RED ){
if( z->p == z->p->p->left ){
Node *y = z->p->p->right;
if( y->color == RED ){ // 父、叔均为红色 则将父父的黑降到父、叔上 重新处理父父
z->p->color = BLACK;
y->color = BLACK;
z->p->p->color = RED;
z = z->p->p;
} else if ( z == z->p->right ) { // 叔节点为黑 且当前为父的右孩子 则转为左孩子处理
z = z->p;
leftRotate( tree, z );
} else { // 叔节点为黑 且当前节点为父节点的左孩子 对父父右旋 并着为红色 将父着为黑色 结束处理
z->p->color = BLACK;
z->p->p->color = RED;
rightRotate( tree, z->p->p );
}
} else { // 父节点为父父的左孩子
Node *y = z->p->p->left; // 获取叔节点记为 y
if( y->color == RED ) { // 叔节点为红色 则将父、叔同着为黑色 将父父着为红色 迭代处理父父
z->p->color = BLACK;
y->color = BLACK;
z->p->p->color = RED;
z = z->p->p;
} else if ( z == z->p->right ) { // 叔节点为黑色 当前节点为父节点的右孩子 则将父父着为红色 将父着为黑色 左旋父父 结束处理
z->p->p->color = RED;
z->p->color = BLACK;
leftRotate( tree, z->p->p );
} else { // 叔节点为黑色 当前节点为父节点的左孩子 右旋父节点 对父节点迭代处理 转为上一情况
z = z->p;
rightRotate( tree, z );
}
}
}
tree->root->color = BLACK;
}
// 红黑树插入 先按二叉搜索树的方法插入节点并置为红色 再调用RB_insert_fixup 修正可能的破坏
void RB_insert( RBTree *tree, int key ){
Node *y = tree->nil;
Node *x = tree->root;
while( x != tree->nil ){
y = x;
x = ( x->key < key ? x->right : x->left );
}
Node *z = malloc( sizeof(Node) );
z->p = y;
z->left = tree->nil;
z->right = tree->nil;
z->key = key;
z->color = RED;
if( y == tree->nil ) {
tree->root = z;
} else if ( key < y->key ) {
y->left = z;
} else {
y->right = z;
}
RB_insert_fixup( tree, z );
}
// 二叉搜索树某个子树的最小值为最左边的节点
Node* RB_minimum( RBTree *tree, Node *node ){
while( node->left != tree->nil ){ // 寻找最左边的节点
node = node->left;
}
return node;
}
// 二叉搜索树某个子树的最大值为最右边的节点
Node* RB_maximum( RBTree *tree, Node *node ){
while( node->right == tree->nil ){ // 寻找最右边的节点
node = node->right;
}
return node;
}
// 中序遍历
void RB_printNode( Node *node, Node *nil ){
if( node != NULL && node != nil ){
RB_printNode( node->left, nil );
printf("%d%c ", node->key, ( node->color == BLACK ? 'B' : 'R' ) );
RB_printNode( node->right, nil );
}
}
void RB_print( RBTree *tree ){
RB_printNode( tree->root, tree->nil );
putchar('\n');
}
// 清空非叶节点的所有内部节点
void clearRBTreeNode( Node *node, Node *nil ){
if( node != NULL && node != nil ){
clearRBTreeNode( node->left, nil );
clearRBTreeNode( node->right, nil );
free( node );
}
}
// 先清空内部节点 再清除叶节点 再清除树结构
void clearRBTree( RBTree *tree ){
clearRBTreeNode( tree->root, tree->nil ); // 清空节点
free( tree->nil );
free( tree );
}
// 获取一个初始化过的红黑树 默认存在叶节点且根节点暂为叶节点
RBTree *getRBTree(){
RBTree *tree = malloc( sizeof(RBTree) ); // 不考虑调用失败
tree->nil = calloc( 1, sizeof(Node) ); // 颜色默认黑色 0
tree->root = tree->nil; // 初始根节点指向叶节点
return tree;
}
运行结果