红黑树模板

本文详细探讨了红黑树的数据结构及其关键操作,包括左旋、右旋、插入和删除。特别地,重点讲解了红黑树删除操作的难点,通过实例代码展示了如何处理删除节点后的平衡调整,确保红黑树性质得以维持。同时提供了完整的C++实现,包括查找、插入和删除等函数,帮助读者深入理解红黑树的工作原理。
摘要由CSDN通过智能技术生成

红黑树这一块的知识有点难懂,,难懂的地方就是左旋,右旋,插入调整,和删除调整,对我来说最难懂的还是删除部分,一直只得形不得意,,看着算法导论眼睛疼。。。这里有一位大佬讲的还行-》https://www.cnblogs.com/skywang12345/p/3245399.html

下面是我实现的代码,不够完善,因为我没有重复使用树根,先这样吧,回头复习时把他完善。。下面代码可以运行的哦,注意引用符号,之前有个地方就是因为我加了个引用就一直栈溢出。。。删除操作部分我注释了的那种方法也是可以的,不过我觉得麻烦。。从网上看到了一种简洁的写法就修改了下

代码:

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
using namespace std;
//红黑树学习,主要难点,左旋,右旋,插入,删除
//红黑树主要是为了保证二叉搜索树的平衡,避免出现一条简单路径的深度高,一条简单路径深度低的情况
//红黑树的效率高,保证在最坏的情况下的时间复杂度的为O(lgn)
//红黑树的五条性质:
//1.每个节点或是红色的,或是黑色的
//2.根节点是黑色的
//3.每个叶节点(NIL)是黑色的
//4.如果一个节点是红色的,则它的两个子结点都是黑色的
//5.对每个结点,从该节点到其所有后代叶节点的简单路径上,均包含相同数目的黑色结点
//*************************数据结构定义阶段*************************
typedef enum{RED,BLACK} Color;

typedef struct BST{
    int key;    //关键字
    Color color;//节点颜色
    struct BST *p,*left,*right; //双亲,左孩子,右孩子
    
    BST(){color = BLACK; p = left = right = NULL;}  //无参构造

}BSTNode;

typedef struct BST_Root{    //树根
    BSTNode *root;
    BSTNode *nil;   //红黑树是以一个标记结尾的

    BST_Root(){     //
        
        nil = new BSTNode();
        nil->color = BLACK; 
        nil->p = nil->left = nil->right = NULL;
        root = nil;
    }

}BSTRoot;
BSTRoot BT;  //这里我直接把树根先建好,后面方便使用nil

//*************************数据结构定义阶段*************************

//*************************查找*************************
//和一般的二叉搜索树一样的操作,只是结尾标志不再是NULL,而是统一的nil

BSTNode* tree_search(BSTNode *&T,int k){    //递归查找

    if(T != BT.nil && T->key != k){
        if(k < T->key)
            return tree_search(T->left,k);
        else
            return tree_search(T->right,k);
    }
    return T;
}

BSTNode* interative_tree_search(BSTNode *&T,int k){     //非递归查找
    BSTNode *t = T;

    while(t != BT.nil && t->key != k){
        if(k < t->key)
            t = t->left;
        else
            t = t->right;
    }
    return t;
}

//*************************查找*************************

//************************最大关键字查询,最小关键字查询,前驱和后继查询************************

BSTNode* tree_minmum(BSTNode *&T){  //最小关键字查询
    BSTNode *t = T;

    while(t!=BT.nil && t->left != BT.nil){
        t = t->left;
    }
    return t;
}

BSTNode* tree_maxmum(BSTNode *&T){  //最大关键字查询
    BSTNode *t = T;

    while(t!=BT.nil && t->right != BT.nil){
        t = t->right;
    }
    return t;
}

BSTNode* tree_successor(BSTNode *&T){   //后继查询,后继就是大于T.x的最小关键字节点
    BSTNode *t = T,*y = BT.nil;
    if(T == BT.nil) return T; 

    if(T->right != BT.nil)          //如果右子树不为空直接求右子树的最小节点
        return tree_minmum(T->right);
    
    y = t->p;

    while(y != BT.nil && y->right == t){
        t = y;
        y = y->p;
    }
    return y;
}

BSTNode* tree_predecessor(BSTNode *&T){ //前驱查询,前驱就是小于T.x的最大关键字节点
    BSTNode *t = T,*y = BT.nil;
    if(T == BT.nil) return T;

    if(T->left != BT.nil)          //如果左子树不为空直接求左子树的最大节点
        return tree_maxmum(T->left);
    
    y = t->p;

    while(y != BT.nil && y->left == t){ //这里就是没有左子树的情况,往上找,看看上面还有没有小于t.x的节点
        t = y;
        y = y->p;
    }
    return y;
}

//************************最大关键字查询,最小关键字查询,前驱和后继查询************************

//************************左旋和右旋************************

void left_rotate(BSTNode *&T,BSTNode * x){

    BSTNode *y = x->right;  //x的右孩子取出来
    x->right = y->left; //把y的左孩子给x当右孩子
    
    if(y->left != BT.nil){  //y的左孩子不是nil就让它认x为爸爸
        y->left->p = x;
    }

    y->p = x->p;    //y认x的爸爸为爸爸

    if(x->p == BT.nil)  //如果x原来是根节点,那么更新根节点
        BT.root = y;
    else if(x == x->p->left)    //看x原来是它爸爸的左孩子还是右孩子,因为从现在开始x不是它原来爸爸的儿子了,y才是
        x->p->left = y;
    else
        x->p->right = y;
    y->left = x;
    x->p = y;
    
}

void right_rotate(BSTNode *&T,BSTNode *x){ //右旋,同上面是对称的
    
    BSTNode *y = x->left;

    x->left = y->right;

    if(y->right != BT.nil)
        y->right->p = x;
    
    y->p = x->p;

    if(x->p == BT.nil)
        BT.root = y;
    else if(x == x->p->left)
        x->p->left = y;
    else
        x->p->right = y;
    y->right = x;
    x->p = y;
}

//************************左旋和右旋************************

//************************插入************************

void RB_Insert_Fixup(BSTNode *&T,BSTNode *&z){
    //有三种情况,1.父节点是黑色的;2.z是根;3.父节点是红色的;三种情况只有3需要调整,1,2种不用
    while(z->p->color == RED){
        //看看父节点是祖父结点的左孩子还是右孩子
        if(z->p == z->p->p->left){
            //取得叔叔结点
            BSTNode *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;
                left_rotate(T,z);
            }else if(z == z->p->left){
                //如果叔叔结点时黑色的,并且当前结点是父节点的左孩子,那么把父节点修改成黑色,祖父结点修改成红色,并对祖父结点右旋
                z->p->color = BLACK;
                z->p->p->color = RED;
                right_rotate(T,z->p->p);
            }
        }else { //下面操作与上面对称
            BSTNode *y = z->p->p->left;

            if(y->color == RED){
                y->color = BLACK;
                z->p->color = BLACK;
                z->p->p->color = RED;
                z = z->p->p;
            }else if(z == z->p->left){
                z = z->p;
                right_rotate(T,z);
            }else if(z == z->p->right){
                z->p->color = BLACK;
                z->p->p->color = RED;
                left_rotate(T,z->p->p);
            }
        }
    }
    BT.root->color = BLACK; //最后把根结点设置成黑色,此时整棵树以满足所有红黑树性质
}

void tree_insert(BSTNode *&T, int k){   //插入操作基本一样,就最后多了两步
    
    BSTNode *t = T,*y = T;

    BSTNode *z = new BSTNode();
    z->key = k;
    z->p = z->left = z->right = BT.nil;
    z->color = RED; //这里要把颜色设置为红色

    while(t!=BT.nil){
        
        y = t;
        if(k < t->key)
            t = t->left;
        else
            t = t->right;
    }

    z->p = y;

    if(y == BT.nil)
        BT.root = z;
    else if(k < y->key)
        y->left = z;
    else
        y->right = z;

    RB_Insert_Fixup(T,z);       //调整颜色,保持红黑性质
}

//************************插入************************


//************************删除************************

void tree_transplant(BSTNode *&T,BSTNode *&u,BSTNode *&v){  

    if(u->p == BT.nil)
        BT.root = v;
    else if(u == u->p->left)
        u->p->left = v;
    else
        u->p->right = v;
    v->p = u->p;
}

void RB_Delete_Fixup(BSTNode *&T,BSTNode *x){

    while(x != T && x->color == BLACK){

        if(x == x->p->left){
            BSTNode *w = x->p->right;

            if(w->color == RED){
                w->color = BLACK;
                x->p->color = RED;
                left_rotate(T,x->p);
                w = x->p->right;        //w是x的兄弟结点,所以这里要修改
            }else if(w->left->color == BLACK && w->right->color == BLACK){

                w->color = RED;
                x = x->p;
            }else if(w->left->color == RED && w->right->color == BLACK){
                w->left->color = BLACK;
                w->color = RED;
                right_rotate(T,w);
                w = x->p->right;
            }
            if(w->right->color == RED){
                w->color = x->p->color;
                x->p->color = BLACK;
                w->right->color = BLACK;
                left_rotate(T,x->p);
                x = T;

            }

        }else{
            BSTNode *w = x->p->left;

            if(w->color == RED){
                w->color = BLACK;
                x->p->color = RED;
                right_rotate(T,x->p);
                w = x->p->left;
            }else if(w->left->color == BLACK && w->right->color == BLACK){
                w->color = RED;
                x = x->p;
            }else if(w->left->color == BLACK && w->right->color == RED){
                w->right->color = BLACK;
                w->color = RED;
                w = x->p->left;
            }
            if(w->right->color == BLACK){
                w->color = x->p->color;
                x->p->color = BLACK;
                w->left->color = BLACK;
                right_rotate(T,x->p);
                x = T;
            }
            
        }
    }
    x->color = BLACK;
}


void tree_delete(BSTNode *&T,int k){  //删除

    BSTNode *z = tree_search(T,k);
    if(z == BT.nil) return;

    BSTNode *y = z,*x = NULL;
    // Color y_color = y->color;

 //如果两个孩子,就找到右子树中最小的结点,将之代替,然后直接删除该结点即可
    if(z->left != BT.nil && z->right != BT.nil){

        y = tree_minmum(z->right);
        z->key = y->key;//这里只对值进行复制,并不复制颜色,以免破坏红黑树的性质
        z = y;
    }
    if(z->left == BT.nil){
        x = z->right;
        tree_transplant(T,z,z->right);        
    }
    else if(z->right == BT.nil){
        x = z->left;
        tree_transplant(T,z,z->left);        
    }
    if(z->color == BLACK)
        RB_Delete_Fixup(T,x);
    delete z;
    // else{

    //     y = tree_minmum(z->right);  //后继
    //     y_color = y->color; 
    //     x = y->right;

    //     if(y->p == z)
    //         x->p = z;
    //     else {  
    //         tree_transplant(T,y,y->right);
    //         y->right = z->right;
    //         y->right->p = y;
    //     }
    //     tree_transplant(T,z,y);
    //     y->left = z->left;
    //     y->left->p = y;
    //     y->color = z->color;
    // }
    // if(y_color == BLACK)
    //     RB_Delete_Fixup(T,x);
}

//************************删除************************

void inorder_input(BSTNode *&T){ //中序遍历输出
    if(T!=BT.nil){
        inorder_input(T->left);
        printf("%d ",T->key);
        inorder_input(T->right);
    }
}

int main(){

    int a[9] = {11,2,14,1,7,15,5,8,4};

    for(int i = 0;i<9;i++)
        tree_insert(BT.root,a[i]);

    printf("inorder input primitive sequence: ");
    inorder_input(BT.root);
    putchar('\n');
    printf("after digit 7 be deleted: ");
    tree_delete(BT.root, 7);
    tree_delete(BT.root,14);
    inorder_input(BT.root);
    putchar('\n');
    system("pause");
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值