《算法導論》筆記 第13章 總結與思考

【總結】


template <class T>
class RBTREE{
public:
    struct NODE{
        NODE *p,*l,*r;
        T key;
        COLOR c;
        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;
        return p;
    }
    void deleteNode(NODE *p) {
        delete p;
    }
    void init() {
        nil = &NIL;
        nil->c = BLACK;
        nil->p = nil;
        nil->l = nil;
        nil->r = nil;
        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;
    }
    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;
    }
/*********************************************/
    void rbInsert(NODE *z) {
        NODE *y = nil;
        NODE *x = root;
        while (x != nil) {
            y = x;
            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;
        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 << 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; }
    }
};




【思考】


13-1 持久動態集合

a) 對一棵通常的持久二叉查找樹,爲插入一個關鍵字 k 或刪除一個結點 y 的,肯定須要改變哪些節點?算法

須要新增從根到插入或刪除的結點的路徑上的全部結點。code


b) 請寫出一個程序PERSISTENT-TREE-INSERT,使得在給定一棵持久樹T和一個要插入的關鍵字k時,它返回將k插入T後的新的持久樹T'。blog


    NODE* persistentTreeInsert(NODE *z,int cnt = -1) {
        if (cnt == -1) cnt = cur;
        NODE *y = nil;
        NODE *x = copyNode(root[cnt]);
        root[++cur] = x;
        while (x != nil) {
            y = x;
            if (z->key < x->key) {
                x->c[0] = copyNode(x->c[0]);
                x = x->c[0];
            }
            else {
                x->c[1] = copyNode(x->c[1]);
                x = x->c[1];
            }
        }
        if (y == nil) { root[cur] = z; }
        else {
            if (z->key < y->key) { y->c[0] = z; }
            else { y->c[1] = z; }
        }
        z->c[0] = nil;
        z->c[1] = nil;
        return root[cur];
    }



c) 若是持久二叉查找樹T的高度爲h,所實現的PERSISTENT-TREE-INSERT的時間和空間要求分別是多少?遞歸

O(h) O(h)ip


d) 假設咱們在每一個結點中增長一個父親結點域。這樣一來,PERSISTENT-TREE-INSERT須要作一些額外的複製工做。證實在這種狀況下,時空要求爲Ω(n),其中n爲樹中結點個數。rem

每次更新都要複製整棵二叉樹,所以時空要求爲Ω(n)。it


e) 說明如何利用紅黑樹來保證每次插入或刪除的最壞狀況運行時間爲O(lgn)。io



13-2 紅黑樹上的鏈接操做

a) 給定一棵紅黑樹T,其黑高度被存放在域bh[T]中。證實在不須要樹中結點的額外存儲空間和不增長漸進運行時間的前提下,能夠用RB-INSERT和RB-DELETE來維護這個域。並證實當沿T降低時,可對每一個被訪問的結點在O(1)時間內肯定其黑高度。class

在fix中向上維護bh[T]便可。file

降低時,遇到黑結點則當前bh-1。


b) 假設bh[T1]>=bh[T2]。請描述一個O(lgn)時間的算法,使之能在T1中從黑高度爲bh[T2]的結點中選出具備最大關鍵字的黑結點y。

儘可能向右兒子找。


c) 設Ty是以y爲根的子樹。說明如何在不破壞二叉查找樹性質的前提下,在O(1)時間裏用Ty∪{x}∪T2來取代Ty。

以x爲根,Ty T2 爲左右子樹。


d) 要保持紅黑性質1),3)和性質5),應將x着什麼顏色?說明如何能在O(lgn)時間內恢復性質2)、4)。

紅色,調用insertFix(x)。


e) 論證b)部分做的假設不失通常性。描述當bh[T1]<=bh[T2]時所出現的對稱狀況。

同b)。


f) 證實RB-JOIN的運行時間是O(lgn)。

插入O(1),恢復O(lgn)。


13-3 AVL樹


a) 證實一棵有n個結點的AVL樹其高度爲O(lgn)。

O(lg2n)


b) 描述一個程序BALANCE(x),輸入一棵以x爲根的子樹,其左子樹與右子樹都是高度平衡的,並且它們的高度差之可能是2,而後將以x爲根的子樹轉變爲高度平衡的。

將x進行左旋右旋來平衡左右子樹。


c) 利用b)來描述一個遞歸程序AVL-INSERT(x,z),輸入AVL樹中的一個結點x以及一個新建立的結點z,而後把z添加到以x爲根的子樹中,並保持x是AVL樹的根的性質。

向下遞歸插入結點z,回溯時如有不平衡的狀況則調用BALANCE(x)。


d) 證實對一棵有n個結點的AVL樹,AVL-INSERT操做要用O(lgn)時間,執行O(1)次旋轉。


13-4 Treap


若是v是u的左孩子,則key[v]<key[u]。

若是v是u的右孩子,則key[v]>key[u]。

若是v是u的孩子,則priority[v]>priority[u]。


a) 證實:給定一個結點集合x1,x2,…,xn,它們關聯了關鍵字和優先級,存在惟一的一棵treap與這些結點相關聯。

因爲樹堆的性質3,根結點一定是最優先的結點,而比跟小的結點在左子樹中,比根大的在右子樹中。

同理左右子結點也一定是子樹中最優先的結點,所以存在惟一的treap。


b) 證實:treap的指望高度是Θ(lgn),所以在treap內查找一個值所花的時間爲Θ(lgn)。


c) 解釋TREAP-INSERT如何工做。解釋其思想並給出僞代碼。

若插入的結點的比父結點更優先,則對父結點左旋/右旋。


d) 證實:TREAP-INSERT的指望運行時間是Θ(lgn)。



e) 考慮利用TREAP-INSERT來插入x後的treap T。令C爲x左子樹的右脊柱的長度。令D爲x右子樹的左脊柱的長度。證實:在插入x的期間所執行的旋轉的總次數等於C+D。



f) 證實X(i,k)=1當且僅當priority[y]>priority[x],key[y]<key[x],並且對於每一個知足key[y]<key[z]<key[x]的z,有priority[y]<priority[z]。



g) 證實:Pr{X_{i,k}=1}=(k-i-1)!/(k-i+1)!=1/(k-i+1)(k-1)


h) 證實: E[C]=sum_{j=1}^{k-1}1/j(j+1)=1-1/k


i) 利用對稱性來證實:E[D]=1-1/(n-k+1)


j) 總結在將一個結點插入一棵treap內時,所執行的旋轉的指望次數小於2。


class Treap{
public:
    struct Node{
        Node* ch[2];//左右子樹
        int fix;//優先級。數值越小,優先級越高
        int key;
        int size;//以它爲根的子樹的總結點數
        int cmp(int x) const{
            if (x==key) return -1;
            return x<key?0:1;
        }
        //名次樹
        void maintain(){
            size=1;
            if (ch[0]!=NULL) size+=ch[0]->size;
            if (ch[1]!=NULL) size+=ch[1]->size;
        }
    };
    Node* root;
    Treap(){
        srand(time(0));
        root=NULL;
    }
    void removetree(Node* &t){
        if (t->ch[0]!=NULL) removetree(t->ch[0]);
        if (t->ch[1]!=NULL) removetree(t->ch[1]);
        delete t;
        t=NULL;
    }
    void clear(){
        srand(time(0));
        removetree(root);
    }
    Node* newNode(int v){
        Node* t=new Node;
        t->key=v;
        t->ch[0]=t->ch[1]=NULL;
        t->fix=rand();
        t->size=1;
        return t;
    }
    //d=0表明左旋,d=1表明右旋
    void rotate(Node* &o,int d){
        Node* k=o->ch[d^1];
        o->ch[d^1]=k->ch[d];
        k->ch[d]=o;
        o->maintain();
        k->maintain();
        o=k;
    }
    //在以o爲根的子樹中插入鍵值x,修改o
    void insert(Node* &o,int x){
        if (o==NULL) o=newNode(x);
        else{
            int d=o->cmp(x);
            if (d==-1) d=1;
            insert(o->ch[d],x);
            if (o->ch[d]->fix<o->fix) rotate(o,d^1);
        }
        o->maintain();
    }
    void remove(Node* &o,int x){
        int d=o->cmp(x);
        if (d==-1){
            Node* u=o;
            if (o->ch[0]!=NULL&&o->ch[1]!=NULL){
                int d2=(o->ch[0]->fix<o->ch[1]->fix?1:0);
                rotate(o,d2);
                remove(o->ch[d2],x);
            }else{
                if (o->ch[0]==NULL) o=o->ch[1];
                else if (o->ch[1]==NULL) o=o->ch[0];
                delete u;
            }
        }
        else remove(o->ch[d],x);
        if (o!=NULL) o->maintain();
    }
    bool find(Node* o,int x){
        while (o!=NULL){
            int d=o->cmp(x);
            if (d==-1) return 1;
            else o=o->ch[d];
        }
        return 0;
    }
    //第k大的值
    int kth(Node* o,int k){
        if (o==NULL||k<=0||k>o->size) return 0;
        int s=(o->ch[1]==NULL?0:o->ch[1]->size);
        if (k==s+1) return o->key;
        else if (k<=s) return kth(o->ch[1],k);
        else return kth(o->ch[0],k-s-1);
    }
    void merge(Node* &src){
        if (src->ch[0]!=NULL) merge(src->ch[0]);
        if (src->ch[1]!=NULL) merge(src->ch[1]);
        insert(root,src->key);
        delete src;
        src=NULL;
    }
};
相關文章
相關標籤/搜索