红黑树

红黑树

红黑树是一种二叉查找树。
一棵二叉查找树若满足如下性质,则为红黑树:
1)每个节点是黑的或是红的;
2)根节点是黑的;
3)每个叶节点是黑的;
4)如果一个节点是黑的,则它的两个孩子都是黑的。
5)对每个节点,从该节点到其子孙节点的路径(一直到叶节点)上包含相同数目的黑节点。
注:叶节点的关键字为空,用NIL表示

黑高度:从某个节点(不包括该节点)到达一个叶节点的任意一条路径上的黑色节点的个数。

定理:一棵有n个内节点的红黑树的高度至多为2lg(n+1).
证明:将红黑树转化为2-3-4树,转化过程如下图所示:
这里写图片描述
假设红黑树中内节点个数为n,高度为h,2-3-4树高度为h1,
在2-3-4树中, 2h1#leaves()4h1 2 h 1 ≤ # l e a v e s ( 叶 节 点 个 数 ) ≤ 4 h 1 ( 单个节点个数大于等于2而小于等于4)
根据定理:一棵平衡树的叶节点数目等于内部节点数+1,所以: 2h1n+14h1 2 h 1 ≤ n + 1 ≤ 4 h 1
hlg(n+1) ⟹ h ≤ l g ( n + 1 ) ,因为红黑树中在一条路径红色节点个数最多占总节点个数一半,所以 h2h1 h ≤ 2 h 1 ,
h2lg(n+1) ⟹ h ≤ 2 l g ( n + 1 )

插入
首先找到要插入的位置将其插入(插入节点z时z被着为红色,key[z] = k),这一步和普通二叉查找树一样,然后,为了保持红黑树的性质,需要调用一个辅助程序RBInsertFixup(T,k)来对节点重新着色并旋转.。时间为O(lgn).
z要设为红色的原因:为了保持性质5,使所有节点黑高度相同。
插入之后,如果p[z]为红色,就违反了性质4:不能出现连续的红色结点。所以RBInsertFixup(T,z)的目的就是纠正这个错误,其中while循环成立的条件即color[p[z]]=RED.当树中只有一个节点的时候,就违反了性质2:根节点是黑的,这个很好解决,主要解决性质4的问题。
RBInsertFixup(T,k)需要处理三种情况:
1)z的叔叔y是红色的:此时可以将p[z]和y都着为黑色已解决z和p[z]都是红色的问题,并将p[p[z]]着为红色(保持黑高度不变),然后将p[p[z]]作为新的z来重复循环

这里写图片描述

2)z的叔叔y是黑色的,而且z是右孩子:对z的父亲进行左旋,使其变为情况三
3)z的叔叔y是黑色的,而且z是左孩子:变换z的父亲和爷爷的颜色,并对爷爷做右旋,这样便不再有两个红色的节点了,循环结束。
这里写图片描述

删除
删除前面的操作也和普通二叉查找树一样,然后调用一个辅助程序RBDeleteFixup(T,k),用来改变节点的颜色并作旋转以保持红黑树性质。时间也为O(lgn).只有当需要删除的节点是黑色时,才需要调用RBDeleteFixup(T,k),因为黑高度变了,破坏了性质5,此时补救的办法是把节点x(key[x] = k)视为还有额外的一重黑色,这样性质5就不会变了,去除额外黑色的方法是在while循环中不断将黑色上移(不断地改变颜色和旋转),直到:
1)x指向一个红色节点,此时将x单独着为黑色;
2)x指向根,此事简单地消除额外的黑色
在while循环中,节点x总是指向具有双重黑色的非根节点。
RBDeleteFixup需要处理四种情况:
1)x的兄弟w是红色的:改变父亲和w的颜色,并对父亲做左旋,此时转为情况2)、3)或4)。
这里写图片描述
2)x的兄弟w是黑色的,而且w的两个孩子都是黑色的:从x和w上去除一重黑色,从而x为黑色而w为红色,为了补偿,p[x]加上一重额外的黑色,令p[x]为新的x继续循环
这里写图片描述
3)x的兄弟w是黑色的,w的左孩子是红色的,右孩子是黑色的:改变w及其左孩子的颜色,并对w进行右旋,红黑性质依然保持,此时进入情况4
这里写图片描述
4)x的兄弟w是黑色的,而且w的右孩子是红色的:改变p[x]和w的颜色,并对p[x]做左旋,此时x的额外黑色可去除而变为单独黑色。此时直接令x=root[T],可结束循环。这里写图片描述
注:这些情况不是相互排斥的,可从一种情况转为另一种情况,也可以直接进入某一情况
(以上皆参考《算法导论》)

代码如下:

#include <cstdio>
#include <cstdlib>
#include <queue>
using namespace std;

typedef struct treeNode *RBtree;
enum kindOfColor{RED, BLACK};

struct treeNode{
    int key;
    RBtree left;
    RBtree right;
    RBtree parent;
    int color;
};

RBtree NIL = (RBtree)malloc(sizeof(struct treeNode));

RBtree search(RBtree T, int k){
    if (T == NIL){
        return NIL;
    }
    RBtree P = T;
    while (P != NIL && P->key != k){
        if (P->key > k){
            P = P->left;
        }
        else{
            P = P->right;
        }
    }
    return P;
}

RBtree makeNode(int k){
    RBtree z = (RBtree)malloc(sizeof(struct treeNode));
    z->key = k;
    z->left = z->right = z->parent = NIL;
    z->color = RED;
    return z;
}

RBtree findMin(RBtree T)
{
    if (T == NIL)
        return NIL;
    RBtree p = T;
    while (p->left != NIL)
    {
        p = p->left;
    }

    return p;
}

//x的后继即具有大于key[x]中的关键字最小的那个节点
//节点的后继主要有两种情况:1。z的右子树非空,则其后继为右子树中最小节点;2.右子树为空,则后继为为x的最小祖先节点
RBtree treeSuccessor(RBtree T, RBtree z){
    if (T == NIL){
        return NIL;
    }
    if (z != NIL){
        if (z->right != NIL){
            return findMin(z->right);
        }
        //寻找z的最小祖先
        RBtree p = z->parent;
        while (p != NIL && p->right == z){
            z = p;
            p = p->parent;
        }
        return p;
    }
    return NIL;
}

//左旋转则左下降
RBtree leftRotate(RBtree *T, RBtree x){
    RBtree y = x->right;
    x->right = y->left;
    if (y->left != NIL){
        y->left->parent = x;
    }
    if (x->parent == NIL){
        *T = y;
        (*T)->parent = NIL;
    }
    else{
        y->parent = x->parent;
        if (x == x->parent->left){
            x->parent->left = y;
        }
        else{
            x->parent->right = y;
        }
    }
    x->parent = y;
    y->left = x;
    return y;
}

//右旋转则右下降
RBtree rightRotate(RBtree *T, RBtree x){
    RBtree y = x->left;
    x->left = y->right;
    if (y->right != NIL){
        y->right->parent = x;
    }
    if (x->parent == NIL){
        *T = y;
        (*T)->parent = NIL;
    }
    else{
        y->parent = x->parent;
        if (x == x->parent->left){
            x->parent->left = y;
        }
        else{
            x->parent->right = y;
        }
    }
    x->parent = y;
    y->right = x;
    return y;
}

RBtree RBInsertFixup(RBtree T, RBtree z){
    while (z->parent->color == RED){
        if (z->parent == z->parent->parent->left){
            RBtree y = z->parent->parent->right;
            //情况一:z的叔叔是红色的,将z的父亲和叔叔变为黑色,爷爷变为红色
            if (y->color == RED){
                z->parent->color = BLACK;
                y->color = BLACK;
                y->parent->color = RED;
                z = z->parent->parent;
            }
            else{
                //情况二:z的叔叔y是黑色的,且z是右孩子。对在的父亲做左旋
                if (z == z->parent->right){

                    z = leftRotate(&T, z->parent);
                    z = z->left;
                }
                //情况二转入情况三:z的叔叔y是黑色的,且z是左孩子。改变z的父亲和爷爷的颜色,并对爷爷做右旋
                z->parent->color = BLACK;
                z->parent->parent->color = RED;
                z->parent = rightRotate(&T, z->parent->parent);
            }

        }
        else{
            RBtree y = z->parent->parent->left;
            if (y->color == RED){//情况一:z的叔叔是红色的
                z->parent->color = BLACK;
                y->color = BLACK;
                y->parent->color = RED;
                z = z->parent->parent;
            }
            else{
                if (z == z->parent->left){//情况二:z的叔叔y是黑色的,且z是左孩子

                    z = rightRotate(&T, z->parent);
                    z = z->right;
                }
                z->parent->color = BLACK;//情况二转入情况三:z的叔叔y是黑色的,且z是右孩子
                z->parent->parent->color = RED;
                z->parent = leftRotate(&T, z->parent->parent);
            }
        }
    }
    T->color = BLACK;
    return T;
}

//先找到要插入的位置,再进行层层向上的修改RBInsertFixup
RBtree RBInsert(RBtree T, int k){
    RBtree z = makeNode(k);
    RBtree x = T;
    RBtree y = NIL;
    while (x != NIL){
        y = x;
        if (x->key > z->key){
            x = x->left;
        }
        else{
            x = x->right;
        }
    }
    z->parent = y;
    if (y == NIL){
        T = z;
    }
    else{
        if (y->key > z->key){
            y->left = z;
        }
        else{
            y->right = z;
        }
    }
    z->left = z->right = NIL;
    z->color = RED;
    T = RBInsertFixup(T, z);
    return T;

}

RBtree RBDeleteFixup(RBtree T, RBtree x){
    RBtree w = NIL; //x的兄弟
    while (x != T && x->color == BLACK){
        if (x == x->parent->left){
            w = x->parent->right;
            if (w->color == RED){//情况一:x的兄弟w是红色的
                w->color = BLACK;
                x->parent->color = RED;
                x->parent->parent = leftRotate(&T, x->parent);
                w = x->parent->right;
            }
            //情况二:x的兄弟w是黑色的,而且w的两个孩子都是黑色的
            if (w->left->color == BLACK && w->right->color == BLACK){
                w->color = RED;
                x = x->parent;
            }
            //情况三:x的兄弟w是黑色的,w的左孩子是红色的,右孩子是黑色的:转为情况四
            if (w->left->color == RED && w->right->color == BLACK){
                w->left->color = BLACK;
                w->color = RED;
                w = rightRotate(&T, w);
            }
            //情况四:w是黑色的,w的右孩子是红色的
            if (w->right->color == RED){
                w->color = x->parent->color;
                x->parent->color = BLACK;
                w->right->color = BLACK;
                x->parent->parent = leftRotate(&T, x->parent);
                x = T;
            }
        }
        else{
            w = x->parent->left;
            if (w->color == RED){//情况一:x的兄弟w是红色的
                w->color = BLACK;
                x->parent->color = RED;
                x->parent->parent = rightRotate(&T, x->parent);
                w = x->parent->left;
            }
            //情况二:x的兄弟w是黑色的,而且w的两个孩子都是黑色的
            if (w->left->color == BLACK && w->right->color == BLACK){
                w->color = RED;
                x = x->parent;
            }
            //情况三:x的兄弟w是黑色的,w的右孩子是红色的,左孩子是黑色的:转为情况四
            if (w->right->color == RED && w->left->color == BLACK){
                w->right->color = BLACK;
                w->color = RED;
                w = leftRotate(&T, w);
            }
            if (w->left->color == RED){
                w->color = x->parent->color;
                x->parent->color = BLACK;
                w->left->color = BLACK;
                x->parent->parent = rightRotate(&T, x->parent);
                x = T;
            }
        }

    }
    x->color = BLACK;
    return T;
}

//先和普通二叉树一样删除节点,再做修改RBDeleteFixup
RBtree RBDelete(RBtree T, int k){
    RBtree z = search(T, k);
    RBtree y = NIL, x = NIL;
    if (z->left == NIL || z->right == NIL){
        y = z;
    }
    else{
        y = treeSuccessor(T, z);
    }
    if (y->left != NIL){
        x = y->left;
    }
    else{
        x = y->right;
    }
    x->parent = y->parent;
    if (y->parent == NIL){
        T = x;
    }
    if (y == y->parent->left){
        y->parent->left = x;
    }
    else{
        y->parent->right = x;
    }
    if (y != z){
        z->key = y->key;
    }
    if (y->color == BLACK){
        T = RBDeleteFixup(T, x);
    }
    return T;
}

void levelOrder(RBtree T){
    queue<RBtree> Q;
    Q.push(T);
    RBtree p;
    printf("层序遍历:");
    while (!Q.empty()){
        p = Q.front();
        if (p->left != NIL){
            Q.push(p->left);
        }
        if (p->right != NIL){
            Q.push(p->right);
        }
        printf("%d ", p->key);
        Q.pop();
    }
    printf("\n");
}

int main(void){
    NIL->left = NIL->right = NIL->parent = NIL;
    NIL->color = BLACK;
    NIL->key = INT_MIN;

    RBtree T = NIL;
    T = RBInsert(T, 16);
    levelOrder(T);
    T = RBInsert(T, 11);
    levelOrder(T);
    T = RBInsert(T, 12);
    levelOrder(T);
    T = RBInsert(T, 1);
    levelOrder(T);
    T = RBInsert(T, 3);
    levelOrder(T);
    T = RBDelete(T, 12);
    levelOrder(T);
    T = RBDelete(T, 16);
    levelOrder(T);
    T = RBDelete(T, 11);
    levelOrder(T);
    T = RBDelete(T, 1);
    levelOrder(T);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值