读书笔记_红黑树_C实现

粗略实现了红黑树,也算对自己花的时间有了点交代,暂且贴上实现代码,有心情再详细写写红黑树的一些思考。

大体是按照书上的思路来写的,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; 
}

运行结果
这里写图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值