算法导论(6) 红黑树

来源:互联网 发布:淘宝发红包的钱在哪里 编辑:程序博客网 时间:2024/06/06 01:54

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)设计一些新操作。

0 0
原创粉丝点击