算法导论(6) 红黑树

1.红黑树

许多“平衡”搜索树中的一种,可以保证在最坏情况下基本动态集合操作的时间复杂度为O(lgn)。一颗红黑树是满足下面红黑性质的二叉搜索树(当一个结点没有子结点或者父结点时,指针设为空,并且将空指针视为叶结点(外部结点),为黑色):
(1)每个结点或是红色的,或是黑色的。
(2)根结点是黑色的。
(3)每个叶结点是黑色的。
(4)如果一个结点是红色的,则它的两个子结点都是黑色的。
(5)对每个结点,从该结点到其所有后代叶结点的简单路劲上,均包含相同数目的黑色结点。这条简单路径上的黑色结点数目成为黑高,红黑树的黑高为根结点的黑高。

红黑树的结点包含5个属性:color, key, left, right, p。一颗有n个内部结点的红黑树的高度至多为2lg(n+1)。因此,SEARCH, MINIMUM, MAXIMUM, SUCCESSOR, PREDECESSOR(与二叉搜索树相同)可在红黑树上O(lgn)/ O(h)时间内执行。

INSERT与DELETE操作也在O(lgn)时间内执行,但可能会破坏掉红黑树的性质,因此需要旋转(ROTATE)操作进行恢复,包括左旋和右旋。对一个结点左旋时,该结点右孩子不能为叶结点;对一个结点右旋时,左孩子不为叶结点。

程序如下:

class Node
{
private:
    int key;//值
public:
    string color;
    Node *left;//左子树
    Node *right;//右子树
    Node *parent;//父结点
    int getkey();
    void setkey(int k);
    Node(int k = 0);
    bool operator == (Node &z);
    Node& operator = (Node &z);

};
//红黑树
class RBTree
{

public:
    Node *root;
    RBTree(){
        root = NULL;
    }
    Node* leftrotate(Node *root, Node *x);
    Node* rightrotate(Node *root, Node *x);
    Node* treeinsert(Node *root, Node *z);//插入结点
    Node* insertfixup(Node *root, Node *z);
    Node* treesearch(Node *root, int k);//查询结点
    void treewalk(Node *root);//遍历
    Node* treemin(Node *root);//寻找结点root下最小值结点
    Node* treemax(Node *root);//寻找结点root下最大值结点
    Node* treesuccessor(Node *x);//寻找结点x的后继结点
    Node* treeprodecessor(Node *x);//寻找结点x的前驱结点
    Node* treetransplant(Node *root, Node *u, Node *v);//在根结点root下移动子树v到u
    Node* treedelete(Node *root, Node *z);//删除结点
    Node* deletefixup(Node *root, Node *z);
};

Node::Node(int k):key(k)
{
    color = "red";
    left = NULL;
    right = NULL;
    parent = NULL;
}
bool Node::operator == (Node &z)
{
    if ((key == z.key)&&(left == z.left)&&(right == z.right)&&(parent == z.parent)&&(color==z.color))
    {
        return true;
    }
    else{
        return false;
    }
}
Node& Node::operator = (Node &z)
{
    delete left;
    delete right;
    delete parent;
    left = z.left;
    right = z.right;
    parent = z.parent;
    key = z.key;
    color = z.color;
    return *this;
}
int Node::getkey()
{
    return key;
}
void Node::setkey(int k)
{
    key = k;
}

Node* RBTree::leftrotate(Node *root, Node *x){
    Node *y = x->right;
    //x的右孩子不为叶结点
    if (y != NULL){
        x->right = y->left;
        if (y->left != NULL){
            y->left->parent = x;
        }
        y->parent = x->parent;
        if (x->parent == NULL){
            root = y;
        }
        else if (x == x->parent->left){
            x->parent->left = y;
        }
        else{
            x->parent->right = y;
        }
        y->left = x;
        x->parent = y;

    }
    return root;
}
Node* RBTree::rightrotate(Node *root, Node *x){
    Node *y = x->left;
    if (y != NULL){
        x->left = y->right;
        if (y->right != NULL){
            y->right->parent = x;
        }
        y->parent = x->parent;
        if (x->parent == NULL){
            root = y;
        }
        else if (x == x->parent->left){
            x->parent->left = y;
        }
        else{
            x->parent->right = y;
        }
        y->right = x;
        x->parent = y;
    }
    return root;
}

//查询、最大值、最小值、前驱、后继函数均可以在O(h)时间内完成,h为树的高度
Node* RBTree::treesearch(Node *root, int k)//查询结点
{
    //与二叉搜索树相同
}


void RBTree::treewalk(Node *root)//中序遍历
{
    if (root != NULL)
    {
        treewalk(root->left);
        cout << root->getkey() << ":"<<root->color <<" ";
        treewalk(root->right);
    }

}
Node* RBTree::treemin(Node *root)//寻找结点root下最小值结点
{
    //与二叉搜索树相同
}
Node* RBTree::treemax(Node *root)//寻找结点root下最大值结点
{
    //与二叉搜索树相同
}
Node* RBTree::treesuccessor(Node *x)//寻找结点x的后继结点
{
    Node *y;
    //如果右孩子不空,则右子树中的最小值即为后继
    if (x->right != NULL){
        return treemin(x->right);
    }
    y = x->parent;
    //如果父结点也为空,无后继结点
    //父结点存在时,后继结点为它的最底层祖先,并且后继结点的左结点也是它的祖先
    //因此循环的终止条件为x == y->right,当x是一个左孩子时终止,x的父结点为后继结点
    while (y != NULL && x == y->right)
    {
        x = y;
        y = y->parent;
    }

    if (y != NULL)
    {
        return y;
    }
    return 0;
}
Node* RBTree::treeprodecessor(Node *x)//寻找结点x的前驱结点
{
    Node *y;
    //如果左孩子存在,前驱结点为左子树中最大值
    if (x->left != NULL){
        return treemax(x->left);
    }
    y = x->parent;
    //与后继结点类似,当x为右孩子时终止,x父结点为前驱结点
    while (y != NULL && x == y->left)
    {
        x = y;
        y = y->parent;
    }
    if (y != NULL)
    {
        return y;
    }
    return 0;
}
Node* RBTree::insertfixup(Node *root, Node *z){
    Node *y=new Node();
    //保证性质4,只有执行情况1时,循环会继续进行,执行2和3时,执行一次后循环就停止
    while (z->parent != NULL&&z->parent->parent != NULL&&z->parent->color == "red"){
        if (z->parent == z->parent->parent->left){
            y = z->parent->parent->right;

                if (y != NULL&&y->color == "red"){
                    //情况1:z的叔结点y是红色,无论z是左孩子还是右孩子,改变父结点、叔结点以及祖父结点的颜色来保证性质5,将z上移两层,重新进入循环
                    z->parent->color = "black";
                    y->color = "black";
                    z->parent->parent->color = "red";
                    z = z->parent->parent;
                }
                else{
                    //情况2:叔结点y是黑色,z是一个右孩子,将z指向父结点,左旋,将情况2转为情况3
                    if (z == z->parent->right)
                    {
                        z = z->parent;
                        root=leftrotate(root, z);
                    }
                    //情况3:叔结点为黑色,z是一个左孩子,改变z的父节点以及祖父结点的颜色,然后右旋
                    z->parent->color = "black";
                    z->parent->parent->color = "red";
                    root = rightrotate(root, z->parent->parent);
                }

        }
        else{
            //与之前过程相同,但左右对换
            y = z->parent->parent->left;

                if (y != NULL&&y->color == "red"){
                    //情况1
                    z->parent->color = "black";
                    y->color = "black";
                    z->parent->parent->color = "red";
                    z = z->parent->parent;
                }
                else{
                    //情况2
                    if (z == z->parent->left)
                    {
                        z = z->parent;
                        root = rightrotate(root, z);
                    }
                    //情况3
                    z->parent->color = "black";
                    z->parent->parent->color = "red";
                    root = leftrotate(root, z->parent->parent);
                }

        }
    }
    //保证性质2
    root->color = "black";
    return root;
}
Node* RBTree::treeinsert(Node *root, Node *z)//插入结点
{
    Node *x, *y = NULL;
    x = root;
    //向下寻找位置
    while (x != NULL)
    {
        y = x;
        if (z->getkey() < x->getkey())
        {
            x = x->left;
        }
        else{
            x = x->right;
        }
    }
    z->parent = y;
    //判断树是否为空
    if (y == NULL)
    {
        root = z;
    }
    else if (z->getkey() < y->getkey())
    {
        y->left = z;
    }
    else{
        y->right = z;
    }
    z->left = NULL;
    z->right = NULL;
    //插入的是红色结点,插入后在最底层,这样至多有一条性质被破坏,性质2或是性质4
    z->color = "red";
    root = insertfixup(root, z);
    return root;
}

Node* RBTree::deletefixup(Node *root, Node *x){
    //当y为黑色时,首先破坏的性质就是,原来包含y结点的路径上黑高少了1,其次就是x移动到y原来位置上后,可能出现x与x.p均为红色的情况
    //书中所说y为根结点,y的红色孩子成为新的根节点,感觉这种情况不存在
    //因为y的孩子替换z时,最后会把z的颜色赋值给y的颜色,假如入要删除的是根结点,这一步骤自然会保证根结点颜色不变
    //假如根结点成为要删除结点的后继,要删除节点一定在根结点左子树中,而这时删除节点一定是左子树中的最大值,一定没有右孩子存在
    //这时,直接拿左孩子替换要删除结点就行,不会存在根结点颜色变化的情况,最多是在修正过程中改变了根结点的颜色
    //处理方案是假设x有额外一层黑色,只要将x原来的颜色去掉或者传递给其他红色的结点即可
    Node *w;
    //如果x是原来是红色的,直接赋值为黑色即可,因此循环条件是x为黑色
    //当x为根结点时,直接赋值为黑色
    while (!(*x == *root) && x->color == "black"){
        //x为左孩子时,为右孩子的时候类似
        if (x == x->parent->left){
            w = x->parent->right;
            //情况1:x的兄弟结点是红色的,经过变色以及旋转将其改变为新的结点并转换为黑色,将情况1转为情况2,3,4
                if (w->color == "red"){
                    w->color = "black";
                    x->parent->color = "red";
                    root = leftrotate(root, x->parent);
                    w = x->parent->right;
                }
                //情况2:x的兄弟结点w是黑色的,并且w两个子结点都是黑色的
                //x和w均为黑色,将x上额外的黑色以及w的黑色都去掉,加在他们的父节点上,将w变为红色即为去掉黑色,然后对新的x重新进行循环
                if ( w->left->color == "black"&&w->right->color == "black"){
                    w->color = "red";
                    x = x->parent;
                }
                else{
                    //情况3:x的兄弟结点w是黑色,w左孩子为红色,右孩子为黑色,交换w与w左孩子的颜色,右旋,转为情况4
                    if ( w->right->color == "black"){
                        w->left->color = "black";
                        w->color = "red";
                        root = rightrotate(root, w);
                        w = x->parent->right;
                    }
                    //情况4:x的兄弟结点是黑色,w右孩子为红色,将其变为黑色,相当于给x.p右子树多加一个黑色
                    //右旋是为了将w的黑色转到x.p右子树,右旋后,w变为原来的x.p,保留原来x.p的颜色,而将原来的x.p变为黑色,转到了的新的x.p的左子树上,也即那一层额外的黑色
                    w->color = x->parent->color;
                    x->parent->color = "black";
                    w->right->color = "black";
                    root = leftrotate(root, x->parent);
                    //将x设置为根结点,退出循环
                    x = root;

            }
        }
        else{
            w = x->parent->left;

                if (w->color == "red"){
                    w->color = "black";
                    x->parent->color = "red";
                    root = rightrotate(root, x->parent);
                    w = x->parent->left;
                }
                if (w->right->color == "black"&&w->left->color == "black"){
                    w->color = "red";
                    x = x->parent;
                }
                else{
                    if (w->left->color == "black"){
                        w->right->color = "black";
                        w->color = "red";
                        root = leftrotate(root, w);
                        w = x->parent->left;
                    }
                    w->color = x->parent->color;
                    x->parent->color = "black";
                    w->left->color = "black";
                    root = rightrotate(root, x->parent);
                    x = root;
                }

        }
    }
    x->color = "black";
    return root;
}
Node* RBTree::treetransplant(Node *root, Node *u, Node *v)//在根结点root下移动子树v到u
{
    //u为根结点
    if (u->parent == NULL)
    {
        root = v;
    }
    else if (u == u->parent->left)
    {
        u->parent->left = v;
    }
    else{
        u->parent->right = v;
    }
    //书中提到为空的时候也要赋值,但没有设置哨兵,只是将NULL当做哨兵来理解,实际上还是要判断
    if (v != NULL){
        v->parent = u->parent;
    }
    return root;
}
Node* RBTree::treedelete(Node *root, Node *z)//删除结点
{
    //y用来指向要删除的结点以及将要移动到删除位置的结点
    Node *y=z;
    //x移动到了y的原始位置,记录该点踪迹,因为它可能破坏树的性质
    Node *x;
    string yOriginalColor = y->color;
    if (z->left == NULL){
        x = z->right;
        //如果左孩子为空,利用右孩子替换,右孩子为空时,相当于直接删除
        root = treetransplant(root, z, z->right);
    }
    else if (z->right == NULL){
        x = z->left;
        //执行此步时,一定只有左孩子,拿左孩子替换即可
        root = treetransplant(root, z, z->left);
    }
    else{
        //左右孩子都存在,首先需要寻找它的后继结点
        y= treemin(z->right);
        yOriginalColor = y->color;
        //此时y没有左孩子
        x = y->right;

        if (!(y == z->right)){
            //如果后继结点不是它的右孩子,后继结点没有左孩子,否则不是后继结点
            //先拿后继结点右孩子替换后继结点,再拿后继结点替换要删除的结点
            root = treetransplant(root, y, y->right);
            //将z的右孩子换到y上,if外的语句把z的左孩子换到y上
            y->right = z->right;
            y->right->parent = y;
        }
        //如果后继结点就是它的右孩子,并且这个右孩子没有左孩子,否则不可能是后继结点,因此直接拿后继结点替换
        treetransplant(root, z, y);
        y->left = z->left;
        y->left->parent = y;
        y->color = z->color;
        //书中设置了x->parent = y是防止x为空时,x.p不存在,进入到修正程序中会有错误,但x为空时,不能出现x->parent
        //x为空也就是哨兵时依旧可能破坏了红黑树的性质,也应该进行进行恢复,后来才发现的,但目前程序由于时间关系中没有写
        //如果y原来的颜色是黑色,需要重新维护红黑树的性质,理由如下:
        //当y为红色时,删除或者移动,首先,树的黑高没有发生变化,y也不是树的根结点,根结点没有变化
        //对于前两种情况,z无孩子或者只有一个孩子的时候,y=z,无孩子,红色删掉不影响,有一个孩子时候,y也不可能是红色,因为黑高不对
        //对于第三种情况中,y为z的后继,y为z的右孩子时,y也不可能为红色,因为y此时没有左孩子,黑高就不对
        //y不是z的右孩子时,y为红色,没有左孩子,其右孩子为黑色,替换它后不影响树的性质,而y移动到z,颜色变为z的颜色,也不会影响性质
        if (x != NULL&&yOriginalColor == "black"){
            root = deletefixup(root, x);
        }
    }
    return root;
}
void main()
{
    RBTree rbtree;
    rbtree.root = new Node(14);
    rbtree.root->color = "red";
    Node z[15] = { 5, 49, 2, 18, 15, 30, 17, 56, 46, 10, 12,  44,35, 37, 19 };
    for (int i = 0; i < 15; i++)
    {
        rbtree.root = rbtree.treeinsert(rbtree.root, &z[i]);
    }
    cout << "中序遍历二叉树:" << endl;
    rbtree.treewalk(rbtree.root);
    cout << endl;
    Node *s;
    s = rbtree.treesearch(rbtree.root, 2);
    if (s == NULL){
        cout << "无" << endl;
    }
    else{
        cout << s->getkey() << endl;
    }
    s=rbtree.treeprodecessor(&z[2]);
    if (s == NULL){
        cout << "无" << endl;
    }
    else{
        cout << s->getkey() << endl;
    }
    s = rbtree.treesuccessor(&z[6]);
    if (s == NULL){
        cout << "无" << endl;
    }
    else{
        cout << s->getkey() << endl;
    }
    cout << "最大值:" << endl;
    cout << rbtree.treemax(rbtree.root)->getkey() << endl;
    cout << "最小值:" << endl;
    cout << rbtree.treemin(rbtree.root)->getkey() << endl;
    rbtree.root = rbtree.treedelete(rbtree.root, &z[12]);
    cout << "中序遍历二叉树:" << endl;
    rbtree.treewalk(rbtree.root);
    cout << endl;

}

需要注意的就是在删除操作的修复程序中,x为空的情况,程序中没有处理,但应该进行处理,主函数中的数据示例就是x出现了为空的情况,最后黑高不正确,有时间再研究。

2.顺序统计树
在每个结点上添加附加信息的一棵红黑树。除包括红黑树的五个属性外还包括另一个属性size,表示以该结点为根的子树(包含自身)的结点数:
x.size=x.left.size+x.right.size+1
一个元素的秩定义为在中序遍历树时输出的位置。中序遍历其实就是从小到大排序,但中间可能出现key值相同的元素,秩不相同。
(1)查找以x为根结点的树中具有给定秩的元素:

OS-SELECT(x,i)
    r=x.left.size+1   //计算x结点的秩
    if i==r
        return x
    elseif i<r      //在x左子树中
        return OS-SELECT(x.left,i)
    else          //在x右子树中
        return OS-SELECT(x.right,i-r)

(2)确定一个元素的秩:

OS-RANK(T,x)
    r=x.left.size+1    //r为以y为根结点的子树中x结点的秩
    y=x
    while y!=T.root
        //当y是其父结点的右孩子时,x的秩要加上y兄弟结点的size再加1,如果y是其父结点的左孩子,则在该子树中已无比它key值更小的结点
        if y==y.p.right      
            r=r+y.p.left.size+1
        y=y.p
    return r

为了能准确的运用上面的函数,必须要再红黑树的基础操作中对size属性进行维护。
对于插入操作,新增加的结点size为1,从根到新增结点的路径上的每一个结点的size+1,对于旋转操作,会使两个结点的size属性失效,x及其孩子结点y,旋转操作的维护是利用下面的代码:
y.size=x.size
x.size=x.left.size+x.right.size+1
对于删除操作,遍历y结点(从树中的原始位置开始)到根结点的简单路径,路径上每个结点的size减1。

3.扩张数据结构
(1)选择一种基础数据结构
(2)确定基础数据结构中要维护的附加信息。
(3)检验基础数据结构上的基础修改操作能否维护附加信息。
(4)设计一些新操作。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值