C语言自顶向下实现红黑树

C语言自顶向下实现红黑树

一、红黑树的介绍
红黑树,一种自平衡的二叉查找树,在每一个结点内部储存节点的颜色信息(红色或者黑色),能够以O(logN)的时间复杂度查找、插入和删除。

红黑树是一种二叉查找树,满足二叉树的一般性质:

  • 对于二叉树的任意一个节点,如果左子树非空,左子树的所有节点的键值均小于该结点;如果右子树非空,右子树所有节点的键值均大于该结点。
  • 树中不存在键值相等的节点。

红黑树虽然在本质上是一棵二叉查找树,但其着色的特性使得红黑树保持了高度平衡性,其高度最高是2log(N+1)。红黑树着色性质如下:

  • 每一个节点或为黑色,或为红色。
  • 根是黑色的。
  • 如果一个节点是红色的,那么它的子节点必为黑色。
  • 从一个节点到一个NULL指针的每一条路径必须包含相同数目的黑色节点。

如图显示一棵红黑树满足以上性质:
在这里插入图片描述
(注:每个叶子节点的两个NULL指针视为黑色)

二、红黑树的旋转
红黑树所需要用到的旋转与二叉查找树相同,分为左旋转和右旋转。
设父亲结点为K2,要旋转的儿子结点为K1。
左旋转:K2变为K1的左儿子,K1的左儿子变为K2的右儿子。
在这里插入图片描述
实现代码如下:

static pRedBlackTree SingleRotateWithRight(pRedBlackTree k2) {
   
   
    //右侧子树单旋转
    pRedBlackTree k1;
    
    k1 = k2->pRight;
    k2->pRight = k1->pLeft;
    k1->pLeft = k2;
    return k1;
}

右旋转:K2变为K1的右儿子,K1的右儿子变为K2的左儿子。
在这里插入图片描述
实现代码如下:

static pRedBlackTree SingleRotateWithLeft(pRedBlackTree k2) {
   
   
    //左侧子树单旋转
    pRedBlackTree k1;
    
    k1 = k2->pLeft;
    k2->pLeft = k1->pRight;
    k1->pRight = k2;
    return k1;
}

三、插入删除前的准备
为了方便插入节点,将根不指向NULL指针,而是指向一个表示空的NullNode节点。同时,这个根不是真正树的根,我将树真正的根表示为假根T->pRight(打印树等情况下传递T->pRight)。

#define ElementType int
#define ColorType char
#define Black 'B'
#define Red 'R'
#define Infinity 999999

typedef struct RedBlackTree {
   
   
    ElementType Element;
    struct RedBlackTree *pLeft;
    struct RedBlackTree *pRight;
    ColorType Color;
}RedBlackNode, *pRedBlackTree;

pRedBlackTree NullNode = NULL;

pRedBlackTree Initialize(void) {
   
   
    pRedBlackTree T;

    if (NullNode == NULL) {
   
   
        NullNode = (pRedBlackTree)malloc(sizeof(RedBlackNode));
        if (NullNode == NULL) {
   
   
            printf("SpaceNotAvailable");
            exit(0);
        }
        NullNode->pLeft = NullNode->pRight = NullNode;
        NullNode->Color = Black;
        NullNode->Element = Infinity;
    }

    //创建头节点
    T = (pRedBlackTree)malloc(sizeof(RedBlackNode));
    if (T == NULL) {
   
   
        printf("SpaceNotAvailable");
        exit(0);
    }
    T->Element = -Infinity;
    T->pLeft = T->pRight = NullNode;
    T->Color = Black;

    return T;
}

四、自顶向下红黑树插入节点
相比于自底向上的写法,自顶向下的红黑树不需要保存父节点,同时可以避免处理X(当前节点)的兄弟为红色的情况,具体操作解释如下:

Step1:设置全局静态变量X(当前节点)、P(父节点)、GP(祖父节点)、GGP(曾祖父节点),设置这些变量的原因是在旋转的过程中需要将旋转过后的子树重新链接到GP上。

Step2:当X的两个儿子均为红色的时候,将两个儿子染为黑色,X染为红色。

注意:此时有可能出现X和X的父节点均为红色的情况,此时违背“红黑树红色节点的子节点必为黑色”的性质,需要进行一系列的旋转来纠正。(若满足此类情况执行Step3,不满足直接执行Step4)

Step3:此时的情况是X和P均为红色,有两种情况分别有不同的处理方式。现只讲解下潜方向为左子树方向的情况,向右子树下潜只是为向左子树下潜的镜像情况,与此类似。
操作会平衡左右子树的黑色节点数,使其与处理前数目相同。
X、P、GP呈一字型:P节点的颜色变为黑色,GP变为红色,右旋转GP节点,使P成为新根节点,GP成为P的右儿子。
GP的右儿子S不可能为红色,因为P为红色,此时S为红色,则其在Step1已经被去除。
在这里插入图片描述

X、P、GP呈之字形:X变为黑色,GP变为红色。先进行P的左旋转,使X、P、GP变为一字型,再进行G的右旋转。
在这里插入图片描述
处理结束后转向Step4。

Step4:继续向下前进,如果查找到有相同键值的节点,直接退出;达到NULL节点,进入第5步;否则,重复Step2-4步。

Step5:由于Step4中没有退出,此时已经到达要插入的位置,插入节点,并设节点为红色。
此时有两种情况:
第一种:插入节点的父亲节点为黑色,插入完成。
第二种:插入节点的父亲节点为红色,连续两个节点为红色,需要进行一次Step3来调整节点的颜色和位置,插入完成。

插入的完整代码如下:

static pRedBlackTree X, P, GP, GGP;//用于储存当前节点、父节点、祖父节点、曾祖父节点

static pRedBlackTree Rotate(ElementType Item, pRedBlackTree Parent) {
   
   
    //进行在节点X处的旋转,Item用于检查儿子
    if (Item < Parent->Element)
        
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值