《算法导论》笔记 第14章 14.1 动态顺序统计

【笔记】

一棵顺序统计量树T通过简单地在红黑树的每个结点存入附加信息而成。在一个结点x内,除了包含通常的红黑树的域key color p left right,还包括域size[x]。这个域中包含以结点x为根的子树的结点数(包括x本身),即子树的大小。如果定义哨兵为0,也就是设置size[nil]为0,则有等式 size[x]=size[left[x]]+size[right[x]]+1。

在出现相等关键字的情况下,定义排序为按中序遍历树时输出的结点位置,以此消除顺序统计树原定义的不确定性。


检索具有给定排序的元素:

过程OS-SELECT(x,i)返回一个指向以x为根的子树中包含第i小关键字的结点的指针。

    NODE* osSelect(NODE* x, int i) {
        int r = x->l->size + 1;
        if (i == r) { return x; }
        else if (i < r) { return osSelect(x->l,i); }
        else { return osSelect(x->r,i-r); }
    }
最坏情况下,OS-SELECT的总时间与树的高度成正比。所以对含n个元素的动态集合,运行时间为O(lgn)。


确定一个元素的秩:

给定指向一顺序统计树T中结点x的指针,过程OS-RANK返回对T进行中序遍历后得到的线性序中x的位置。

    int osRank(NODE* x) {
        int r = x->l->size + 1;
        NODE *y = x;
        while (y != root) {
            if (y == y->p->r) { r += y->p->l->size + 1;}
            y = y->p;
        }
        return r;
    }
x的秩可以视为在对树的中序遍历中,排在x之前的结点个数再加上1。

运行时间与树的高度成正比:对含n个结点的顺序统计树时间为O(lgn)。


对子树规模的维护:

插入:第一阶段沿根下降,将新结点插入为某个已有结点的孩子。第二阶段沿树上升,做颜色修改和旋转保持性质。

第一阶段:对路径上的每个结点都要增加其size。

第二阶段:对旋转增加两行代码:

        y->size = x->size;
        x->size = x->l->size + x->r->size + 1;
删除:第一阶段对查找树进行操作。第二阶段至多三次旋转。
要删除结点y,遍历一条由y到根的路径,减少路径上每个结点的size。


#define COLOR int
#define BLACK 1
#define RED 0
using namespace std;

template <class T>
class RBTREE{
public:
    struct NODE{
        NODE *p,*l,*r;
        T key;
        COLOR c;
        int size;
        NODE() {}
    }NIL;
    NODE *nil, *root;
    NODE* newNode(T k, COLOR cl = RED) {
        NODE *p = new NODE;
        p->c = cl;
        p->p = nil;
        p->l = nil;
        p->r = nil;
        p->key = k;
        p->size = 1;
        return p;
    }
    void deleteNode(NODE *p) {
        delete p;
    }
    void init() {
        nil = &NIL;
        nil->c = BLACK;
        nil->p = nil;
        nil->l = nil;
        nil->r = nil;
        nil->size = 0;
        root = nil;
    }
    RBTREE () { init(); }
/*********************************************/
    void leftRotate(NODE *x) {
        NODE *y = x->r;
        x->r = y->l;
        if (y->l != nil) { y->l->p = x; }
        y->p = x->p;
        if (x->p == nil) { root = y; }
        else {
            if (x == x->p->l) { x->p->l = y; }
            else { x->p->r = y; }
        }
        y->l = x;
        x->p = y;
        y->size = x->size;
        x->size = x->l->size + x->r->size + 1;
    }
    void rightRotate(NODE *x) {
        NODE *y = x->l;
        x->l = y->r;
        if (y->r != nil) { y->r->p = x; }
        y->p = x->p;
        if (x->p == nil) { root = y; }
        else {
            if (x == x->p->l) { x->p->l = y; }
            else { x->p->r = y; }
        }
        y->r = x;
        x->p = y;
        y->size = x->size;
        x->size = x->l->size + x->r->size + 1;
    }
/*********************************************/
    void rbInsert(NODE *z) {
        NODE *y = nil;
        NODE *x = root;
        while (x != nil) {
            y = x;
            y->size += 1;
            if (z->key < x->key) { x = x->l; }
            else { x = x->r; }
        }
        z->p = y;
        if (y == nil) { root = z; }
        else {
            if (z->key < y->key) { y->l = z; }
            else { y->r = z; }
        }
        z->l = nil;
        z->r = nil;
        z->c = RED;
        rbInsertFixup(z);
    }
    void rbInsertFixup(NODE *z) {
        NODE *y;
        while (z->p->c == RED) {
            if (z->p == z->p->p->l) {// z 的父亲是爷爷的左儿子
                y = z->p->p->r;// z 的叔叔 y
                if (y->c == RED) {// case 1:叔叔是红的
                    z->p->c = BLACK;// 将 z 的父亲与叔叔置为黑
                    y->c = BLACK;
                    z->p->p->c = RED;// 将 z 的爷爷置为红
                    z = z->p->p;// 问题上移两层
                }
                else {
                    if (z == z->p->r) {// case 2:z 是右儿子
                        z = z->p;
                        leftRotate(z);// 左旋,转为 case 3
                    }
                    z->p->c = BLACK;// case 3:z 是左儿子,对z的爷爷做一次右旋即可完成维护
                    z->p->p->c = RED;
                    rightRotate(z->p->p);
                }
            }
            else if (z->p == z->p->p->r) {// z 的父亲是爷爷的右儿子
                y = z->p->p->l;// z 的叔叔 y
                if (y->c == RED) {// case 1:叔叔是红的
                    z->p->c = BLACK;// 将 z 的父亲与叔叔置为黑
                    y->c = BLACK;
                    z->p->p->c = RED;// 将 z 的爷爷置为红
                    z = z->p->p;// 问题上移两层
                }
                else {
                    if (z == z->p->l) {// case 2:z 是左儿子
                        z = z->p;
                        rightRotate(z);// 右旋,转为 case 3
                    }
                    z->p->c = BLACK;
                    z->p->p->c = RED;
                    leftRotate(z->p->p);
                }
            }
        }
        root->c = BLACK;
    }
/*********************************************/
    NODE* treeMinimum(NODE *rt) {
        while (rt->l!=nil) rt=rt->l;
        return rt;
    }
    NODE* treeMaximum(NODE *rt) {
        while (rt->r!=nil) rt=rt->r;
        return rt;
    }
    NODE* treeSuccessor(NODE *rt) {
        if (rt->r!=nil) return treeMinimum(rt->r);
        NODE* pt=rt->p;
        while (pt!=nil && rt==pt->r) {
            rt=pt;
            pt=pt->p;
        }
        return pt;
    }
    NODE* treePredecessor(NODE *rt) {
        if (rt->l!=nil) return treeMaximum(rt->l);
        NODE* pt=rt->p;
        while (pt!=nil && rt==pt->l) {
            rt=pt;
            pt=pt->p;
        }
        return pt;
    }
/*********************************************/
    NODE* rbDelete(NODE *z) {
        NODE *y, *x, *t;
        t = z;
        while (t != root) {
            t = t->p;
            t->size -= 1;
        }
        if (z->l == nil || z->r == nil) { y = z; }
        else { y = treeSuccessor(z); }
        if (y->l != nil) { x = y->l; }
        else { x = y->r; }
        x->p = y->p;
        if (y->p == nil) { root = x; }
        else {
            if (y == y->p->l) { y->p->l = x; }
            else { y->p->r = x; }
        }
        if (y != z) {
            z->key = y->key;
            // copy y's satellite data into z
        }
        if (y->c == BLACK) { rbDeleteFixup(x); }
        return y;
    }
    void rbDeleteFixup(NODE *x) {
        NODE *w;
        while (x != root && x->c == BLACK) {
            if (x == x->p->l) {// x 为左儿子
                w = x->p->r;// w 是 x 的兄弟
                if (w->c == RED) {// case 1:w 为红色,必有黑色儿子
                    w->c = BLACK;
                    x->p->c = RED;
                    leftRotate(x->p);
                    w = x->p->r;// x 的新兄弟必为黑色
                }
                if (w->l->c == BLACK && w->r->c == BLACK) {// case 2:x 的兄弟 w 是黑色的,w 的两个儿子都是黑色
                    w->c = RED;// 去掉一重黑色
                    x = x->p;// 以 x 父亲重复 while 循环
                }
                else {
                    if (w->r->c == BLACK) {// case 3:x 的兄弟 w 是黑色的,w 的左儿子是红色的,右儿子是黑色
                        w->l->c = BLACK;// 交换 w 与左儿子的颜色
                        w->c = RED;
                        rightRotate(w);// w 右旋
                        w = x->p->r;// 新兄弟是一个有红色右孩子的黑结点
                    }
                    w->c = x->p->c;// case 4:x 的兄弟 w 是黑色的,而且 w 的右儿子是红色的
                    x->p->c = BLACK;
                    w->r->c = BLACK;
                    leftRotate(x->p);
                    x = root;
                }
            }
            else if (x == x->p->r) {// x 为右儿子
                w = x->p->l;// w 是 x 的兄弟
                if (w->c == RED) {// case 1:w 为红色,必有黑色儿子
                    w->c = BLACK;
                    x->p->c = RED;
                    rightRotate(x->p);
                    w = x->p->l;// x 的新兄弟必为黑色
                }
                if (w->l->c == BLACK && w->r->c == BLACK) {// case 2:x 的兄弟 w 是黑色的,w 的两个儿子都是黑色
                    w->c = RED;// 去掉一重黑色
                    x = x->p;// 以 x 父亲重复 while 循环
                }
                else {
                    if (w->l->c == BLACK) {// case 3:x 的兄弟 w 是黑色的,w 的右儿子是红色的,左儿子是黑色
                        w->r->c = BLACK;// 交换 w 与右儿子的颜色
                        w->c = RED;
                        leftRotate(w);// w 左旋
                        w = x->p->l;// 新兄弟是一个有红色左孩子的黑结点
                    }
                    w->c = x->p->c;// case 4:x 的兄弟 w 是黑色的,而且 w 的左儿子是红色的
                    x->p->c = BLACK;
                    w->l->c = BLACK;
                    rightRotate(x->p);
                    x = root;
                }
            }
        }
        x->c = BLACK;
    }
/*********************************************/
    NODE* treeSearch(NODE *rt,T k) {
        if (rt==nil || k==rt->key) return rt;
        if (k<rt->key) return treeSearch(rt->l,k);
        else return treeSearch(rt->r,k);
    }
    void remove(T key) {
        NODE *p = treeSearch(root,key);
        if (p != nil) p = rbDelete(p);
        deleteNode(p);
    }
/*********************************************/
    void insert(T key) {
        rbInsert(newNode(key));
    }
    void showAll(NODE *p) {
        if (p != nil) {
            std::cout << "key = " << p->key << ", color = " << p->c << ", size = " << p->size << std::endl;
            std::cout << "LEFT:" << std::endl;
            showAll(p->l);
            std::cout << "RIGHT:" << std::endl;
            showAll(p->r);
            std::cout << "END." << std::endl;
        }
        //else { std::cout << " NIL " << endl; }
    }
/*********************************************
    数据结构的扩张
*********************************************/
    NODE* osSelect(NODE* x, int i) {
        int r = x->l->size + 1;
        if (i == r) { return x; }
        else if (i < r) { return osSelect(x->l,i); }
        else { return osSelect(x->r,i-r); }
    }
    int osRank(NODE* x) {
        int r = x->l->size + 1;
        NODE *y = x;
        while (y != root) {
            if (y == y->p->r) { r += y->p->l->size + 1;}
            y = y->p;
        }
        return r;
    }
};



【练习】


14.1-1 说明OS-SELECT(x,10)作用于图中红黑树T的过程。


14.1-2 说明OS-RANK(x)作用于图中红黑树T及关键字key[x]为35的结点x上的过程。


14.1-3 写出OS-SELECT的非递归形式。

    NODE* osSelect2(NODE* x, int i) {
        if (x == nil) return x;
        int r = x->l->size + 1;
        while (i != r) {
            if (i < r) { x = x->l; }
            else {
                x = x->r;
                i -= r;
            }
            if (x == nil) break;
            r = x->l->size + 1;
        }
        return x;
    }

14.1-4 写出一个递归过程OS-KEY-RANK(T,k),使之以一棵顺序统计树T和某个关键字k为输入,返回在由T表示的动态集合中k的秩。假设T的所有关键字都是不同的。

    int osKeyRank(NODE* x, int key) {
        if (x == nil) return -1;
        if (x->key == key) return osRank(x);
        else if (key < x->key) return osKeyRank(x->l,key);
        else return osKeyRank(x->r,key);
    }

14.1-5 给定含n个元素的顺序统计树中的一个元素x和一个自然数i,如何在O(lgn)时间内,确定x在该树的线性序中第i个后继?

先找到x的秩rank,再找树中rank+i小的元素。


14.1-6 在OS-SELECT或OS-RANK中,每次引用结点的size域都仅是为了计算在以结点为根的子树中该结点的秩。假设我们将每个结点的秩(对于以该结点为根的子树而言)存于该结点自身之中。说明在插入和删除时如何来维护这个信息(注意这两种操作可能会引起旋转)。

插入时,若路径经过某结点的左儿子,则该结点秩+1。删除时,若路径经过某结点的左儿子,则该结点秩-1。

对x右旋,则x的秩减去左儿子的秩;对x左旋,则x右儿子的秩加上x的秩。


14.1-7 说明如何在O(nlgn)的时间内,利用顺序统计树为大小为n的数组中的逆序对进行计数。

设n个数为a1,a2,a3...ai...an。依次求出以ai为结尾的逆序对数 vi = <aj,ai> (j<i, aj>ai)。

则sum{vi}为逆序数。

依次将ai插入统计树,并求出它们的秩r,则vi=i-r(若aj比ai先插入则j<i,若aj大于ai大则ai的秩小于aj)。


*14.1-8 现有一个圆上的n条弦,每条弦都是按其端点来定义的。请给出一个能在O(nlgn)时间内确定圆内相交弦的对数。

将2n个端点按从[0,2π)小到大排序,扫描端点。

若端点对应的弦未出现,则将其插入树中;

若出现过,则统计有多少端点比该弦的起点大,计入Vi,将起点从树中删去。

则sum{Vi}即为相交弦的对数。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值