红黑树

一.简介

1.1定义

红黑树(red black tree)是BST的一种,AVL的变种。其操作在最坏的情况下为O(logN),而且其插入操作可以相对容易的非递归的实现。红黑树定义如下:

  1. 红黑树是节点为红/黑色的BST:左小右大、中根遍历有序;
  2. root是黑色的;
  3. 红节点的子节点必须是黑色的,反之不必成立
  4. 每个节点到null指针的路径必须包含相同颜色数目的黑节点

由着色法则可推出红黑树的最大高度是2log(N+1);

[补充]

  • BST:每个节点左子树所有节点值小于他,右子树所有节点相反;
  • AVT:每个节点高度相差不超过1;

二.基本操作

同其他树一样,红黑树的基本操作也是插入、查找、删除。红黑树难的操作是插入一个节点。

向红黑树插入节点时,如果放在叶子节点处:

  1. 如果父节点是黑色的,则插入节点为红色,success;
  2. 如果父亲节点是红色的,如果插入节点如果是黑色则会违反规则4——产生节点到null指针路径上的黑色节点不相同,如果插入节点红色,则违反规则3——红色节点子节点必是黑色,即红红不相连。此时可以通过改变树节点的颜色和旋转树完成插入操作
2.1自底向上的插入

插入节点父节点的兄弟节点为黑色(null也是黑色)的前提下:插入节点初始颜色为红色,然后通过“旋转+变色”的操作来保持红黑树的特性,变色指进行旋转的节点变成另一种颜色。插入节点如果父节点为红色一般分为两种情况:

  1. 插入节点X、父亲节点P和祖父节点G是一字型,此时P+G旋转并变色即可;
  2. X、P和G如果是之字形,则X+P先旋转变色,然后X+G旋转变色即可。示意图如下:
2.2自顶向下的红黑树_插入

2.1的前提是保证付P的兄弟节点S是黑色——对红黑树应用自顶向下就是保证S是黑色的过程。详情如下:

自顶向下寻找X插入的位置,这个过程中:

  1. 如果当前节点两个Q的两个孩子节点都是红色,则Q变为红色,两个孩子变为黑色;
  2. 在执行1后,如果Q的父节点Qp是红色,则执行2.1所说的(单/双)旋转——Qp的另一个孩子不可能与Qp同为红色;

通过以上逻辑找到插入位置时,X的父亲节点和叔叔节点不可能同为红色的了。示例图如下(双圈代表红色),自顶向下插入45:

2.3一次自顶向下的基本操作:递归遍历、初始化、单旋转、插入
1)插入

遍历代码如下:

//中根遍历:打印元素递增
void print(RedBlackTree T){
    DoPrint(T->Right);
}

static void DoPrint(RedBlackTree T){
    if(T!=NullNode){
        DoPrint(T->Left);
        Output(T->Element);
        DoPrint(T->Right);
    }
}

2)节点的声明和树的初始化

有两个标记节点:一个是跟标记,存放-∞,其右子节点指向真正的根,一个是NullNode,指代叶子节点的下一个节点,也就是Null

//颜色类型
typedef enum ColorType{Red,Black} ColorType;
//应该还有两行书上没写的,以下
//typedef struct RedBlackNode *RedBlackTree;
//typedef struct RedBlackNode *Position;

//红黑树节点
struct RedBlackNode{
    ElementType Element;
    RedBlackTree Left;
    RedBlackTree Right;
    ColorType Color;
}

Position NullNode=NULL;

//初始化
RedBlackTree Initialize(void ){
    RedBlackTree T;
    
    //如果NullNode为空:黑色、负无穷大、左右指向自身
    if(NullNode==NULL){
        NullNode==malloc(sizeof(struct RedBlackNode));
        NullNode->Left=NullNode->Right=NullNode;//NullNode左右节点都指向自身;
        NullNode->Color=Black;//NullNode颜色为黑色;
        NullNode->Element=Infinity;//也就是∞
    }
    
    T=malloc(sizeof(struct RedBlackNode));
    T->Element=NegInfinity;//也就是-∞
    T->Left=T->Right=NullNode;
    T->Color=Black;
    
    return T;
}

3)节点的旋转

因为得到的数必须连接到父节点上,所以Rotate吧父节点作为一个参数;

static Postion Rotate(ElementType Item,Position Parent){
    if(Item < Parent->Element){
        return Parent->Left=Item < Parent->Left->Element?singleRotateWithLeft(Parent->Left):SingleRotateWithRight(Parent->Left);
    }
    else
        return Parant->Right=Item < Parent->Right->Element?SingleRotateWithLeft(Parent->Right):SingleRotateWithRight(Parent->Right);
}

至于SingleRotateWithLeft函数,猜想其代码如下(参考P89):

static Position SingleRotateWithLeft(Position K2){
    //旋转
    Posotion K1=K2.left;
    K2->Left=K1.Right;
    K1.Right=K2;
    
    //TODO K1、K2变成另一种颜色
    return K1;
}
3)插入过程
static Position X,P,GP,GGP;

//翻转颜色
static void HandleReorient(ElementType Item,RedBlackTree T){
    //颜色反转
    X->Color=Red;
    X->Left->Color=Black;
    X->Right->Color=Black;
    
    if(P->Color=Red){//如果X的父亲也是红色的,此时应该翻转
        GP->Color=Red;
        //如果Item不同时小于P和GP,即父节点和祖父节点
        if((Item < GP->Element)!=(Item<P->Element))//start double ratation
            P=Rotate(Item,GP);
        X=Rotate(Item,GGP);
        X->Color=Black;
    }
    T->Right->Color=Black;//使根节点为黑色
}

//插入节点
RedBlackTree Insert(ElementType Item,RedBlackTree T){
    X=P=GP=T;
    NullNode->Element=Item;
    while(X->Element!=Item){//开始下滤
        GGP=Gp;
        Gp=P;
        P=X;
        if(Item < X->Element)
            X=X->Left;
        else
            X=X->Right;
        if(X->Left->Color==Red&&X->Right->Color==Red)
            HandleReorient(Item,T);
    }//end fo 下滤
    
    if(X!=NullNode)
        return NullNode;
    
    X=malloc(sizeof(struct RedBlackNode));
    X->Element=Item;
    X->Left=X->Right=NullNode;
    
    if(Item<P->Elemnet)
        P->Left=X;
    else
        P->Right=X;
    HandleReorient(Item,T);
    
    return T;
}
2.4删除节点

内容来自网上

BST deletion

首先回顾如果我们是在BST——T上删除目标值t的操作步骤:如果$n_t$(存放目标值t的节点)至多只有一个孩子,则用其孩子或者"terminal_node"代替;如果$n_t$有两个孩子,则用$n_t$的后继节点——即右子树最小的节点s(t)代替——而s(t)则用上边的方法删除——用其唯一子节点或者"terminal_node"代替。

在回顾红黑树的原则:

  • 每个节点到其可达叶子节点经过的黑节点是一样的;

而我们用BST的方式删除节点时,至少会缩短一个节点到叶子节点的路径长度。如果所删除的接单是红色的,则不会破坏以上红黑树原则,黑色则会破坏其原则。

The Approach

我们删除节点的方式是:如果被删除的节点时红色则直接用BST的方式删除就好。如果是红色的,则:

posted on 2018-04-21 14:12  coderDu  阅读(147)  评论(0编辑  收藏  举报