ACM:平衡樹(2)——Splay__acm

來源:互聯網
上載者:User
題目來源: 
        HihoCoder1329
 
題目描述:
    定義一個包含數位集合,集合的初始情況為空白。給定下面兩種操作:
        插入:向集合中添加一個數字k。
        詢問:詢問集合中不超過k的最大數字。
        刪除:刪除落在區間[a, b]內的所有數字。
題目要求對於每次詢問,輸出對應的答案。

解答:
    本題和HihoCoder1325類似,可以用之前介紹的Treap演算法來解答。但Treap樹堆有一個問題,節點的權值是隨機產生的,因此對樹的調整操作也是隨機發生的,在某些情況下,Treap樹同樣也可能會退化為一條線,導致搜尋效率降低。另外,本題添加了刪除指定區間資料的操作,Treap樹中對於刪除操作也比較麻煩。
    這裡介紹另外一種平衡搜尋樹——Splay。

Splay樹的操作:
    Splay樹和普通的二叉搜尋樹類似,Splay中文可以譯為“伸展樹”,它在基礎的二叉搜尋樹的基礎上,定義了4種新的操作,定義如下:
    zig:
    zig操作將樹中的某個節點通過旋轉提升一層,如下圖:           具體來說:如果當前節點是其父節點的左孩子,則右旋,否則,左旋。zig操作後,動作節點提升一層。
    zig-zig:
    zig-zig操作將某個節點高度提升2層,執行該步驟的前提是:當前節點的父節點,以及當前節點的父節點的父節點均不為空白,並且當前節點和其父節點同時為各自父節點的左孩子或右孩子。此時首先對於當前節點的父節點執行zig操作,然後在對當前節點執行zig操作。注意不是對於當前節進行兩次zig操作,如下圖:
    zig-zig操作將節點高度提升2層。
    zig-zag:
    zig-zag操作和zig-zig操作類似。同樣可以將節點高度提升2層。但該操作的前提是:當前節點的父節點,以及當前節點的父節點的父節點均不為空白,並且當前節點和其父節點不是同時為各自父節點的左孩子或右孩子。即:當前節點是其父節點的左孩子並且父節點是其父節點的右孩子,或者當前節點是其父節點的右孩子並且父節點是其父節點的左孩子。zig-zag操作對當前節點連續執行兩次zig操作將,節點高度提升2層。如下圖:           
    splay:
    基於以上的3種操作,我們可以定義Splay樹的splay操作。該操作涉及到了兩個節點A、B,其中節點A是節點B的祖先節點。通過Splay操作,可以將節點B轉化為節點A的子節點。該過程通過對節點B反覆進行以上的3中操作,使節點B的高度逐漸提升,直到節點B是節點A的孩子節點。具體步驟如下:
    ① 判定節點B的父節點是否為節點A。如果是,則演算法結束,否則執行步驟②
    ② 找到節點B的父節點P,判定節點P的父節點是否為節點A。如果是,則對節點B執行zig操作;如果不是,則根據節點B、P以及P的父節點的關係,對節點B執行zig-zig操作或zig-zag操作。
    ③ 反覆執行步驟①和②,直到演算法結束。
    注意:執行該操作的前提是:A節點必須是B節點的祖先節點。如果該條件不滿足,則不可以執行splay操作。另外,如果想將某個節點變為根節點,則只要對空節點NULL和當前節點執行Splay操作即可。

·前驅和後繼:
    在解答本題過程中,用到操作還有尋找某個節點的前驅節點和後繼節點。該過程和普通的二叉尋找樹完全相同,簡要說明如下:
    對於某個節點的前驅節點,就是它的左孩子的“最右下節點”,即從該節點的左孩子開始,順著右孩子指標依次向下尋找,最後找到的節點就是其前驅節點:           如果該節點沒有左孩子,則其前驅節點就在其祖先節點中,是其“最左上祖先節點”,即從該節點開始,依次順著父節點指標向上搜尋,直到當前節點是其父節點的右孩子,則所求前驅節點為當前的父節點:
    對於後繼節點,則和前驅節點正好相反,首先尋找其右孩子的“最左下節點”,如果節點沒有右孩子,則尋找其“最左上祖先節點”。   
·插入:
    對於插入操作,和傳統的二叉尋找樹的插入操作完全相同。不同的是,在插入操作完畢後對插入的節點執行Splay操作,使其成為當前Splay樹的根節點。這樣做的目的是平攤整個演算法中不同操作的執行時間,使得整個演算法的平攤效率趨近於O(lgn)。

·尋找:
    尋找過程和普通的二叉尋找樹的過程也相同。在尋找完成後,對當前找到的節點執行一次Splay操作,使其成為根節點,原因同上。

·刪除:
    刪除操作比較複雜,也是Splay樹的優越性的所在。根據題意,這裡的刪除的操作是指刪除樹中所有落在指定區間[a,b]之內的節點。具體的操作步驟是:首先找出結點a的前驅結點aPre,以及結點b的後繼節點bNext,然後利用Splay操作將aPre轉化為樹的根節點,然後將bNext節點轉化為aPre的孩子節點。此時由於aPre < bNext, 因此,bNext一定是aPre的右孩子節點,此時,位於bNext的左子樹中的節點就一定比apre大,同時又比bNext小,即落入區間[a,b]的節點,此時直接刪除bNext的左子樹即可。   ·虛節點的問題:
    刪除節點的過程中可能會出現區間邊界a,b不在樹中的情況。 此時為了保證演算法可以正確的進行。可以將a,b作為兩個節點插入到樹中,然後再執行刪除的演算法操作。另外在尋找前驅和後繼節點的過程中,也會出現尋找結果為空白的情況,我們可以預先在樹中插入一個極小的數和極大的數,這樣就保證了所有有效節點的前驅和後繼節點均為有效節點。保證演算法的順利進行。
    但是注意,上文中插入的這些節點均不是有效資料節點,是起到輔助作用的“虛”節點,因此在查詢過程中,這些節點的值並不是有效,應該略去。 

輸入輸出格式:
    輸入:
          
第1行:1個正整數n,表示運算元量;
           第2..n+1行:可能包含下面3種規則:
 1個字母'I',緊接著1個數字k,表示插入一個數字k到樹中,保證每個k都不相同。
 1個字母'Q',緊接著1個數字k。表示詢問樹中不超過k的最大數字;
 1個字母'D',緊接著2個數字a,b,表示刪除樹中在區間[a,b]的數。     輸出:
          若干行:每行1個整數,表示針對詢問的回答,保證一定有合法的解。

資料範圍:
     100≤n≤200,000 
     1≤k≤1,000,000,000

程式碼:

/****************************************************//* File        : Hiho_Week_103                      *//* Author      : Zhang Yufei                        *//* Date        : 2016-07-09                         *//* Description : HihoCoder ACM program. (submit:g++)*//****************************************************/#include<stdio.h>#include<stdlib.h>#include<string.h>/* * Define the node in splay tree. * Parameters: *@value: The value of this node. *@max: The max value in the sub-tree. *@tag: Mark if the node is real or fake. *@left & @right: The left and right child. *@parent: The parent node of this node. */typedef struct node {int value;int max;int tag;struct node *left, *right;struct node *parent; } node;// The root of splay tree.node* root;/* * This function define the left rotate operation. * Parameters: *@cur: The current node to rotate. * Returns: *None. */void left_rotate(node* cur) {node* right = cur->right;node* right_left = right->left;node* parent = cur->parent;right->parent = cur->parent;if(parent != NULL) {if(cur == parent->left) {parent->left = right;} else {parent->right = right;}} else {root = right;}right->left = cur;cur->parent = right;cur->right = right_left;if(right_left != NULL) {right_left->parent = cur;}if(cur->tag == 1) {cur->max = cur->value;} else {cur->max = 0;}if(cur->left != NULL) {if(cur->left->max > cur->max) {cur->max = cur->left->max;}}if(cur->right != NULL) {if(cur->right->max > cur->max) {cur->max = cur->right->max;}}if(right->tag == 1) {right->max = right->value;} else {right->max = 0;}if(right->left != NULL) {if(right->left->max > right->max) {right->max = right->left->max;}}if(right->right != NULL) {if(right->right->max > right->max) {right->max = right->right->max;}}}/* * This function define the right rotate operation. * Parameters: *@cur: The current node to rotate. * Returns: *None. */void right_rotate(node* cur) {node* left = cur->left;node* left_right = left->right; node* parent = cur->parent;left->parent = cur->parent;if(parent != NULL) {if(cur == parent->left) {parent->left = left;} else {parent->right = left;}} else {root = left;}left->right = cur;cur->parent = left;cur->left = left_right;if(left_right != NULL) {left_right->parent = cur;}if(cur->tag == 1) {cur->max = cur->value;} else {cur->max = 0;}if(cur->left != NULL) {if(cur->left->max > cur->max) {cur->max = cur->left->max;}}if(cur->right != NULL) {if(cur->right->max > cur->max) {cur->max = cur->right->max;}}if(left->tag == 1) {left->max = left->value;} else {left->max = 0;}if(left->left != NULL) {if(left->left->max > left->max) {left->max = left->left->max;}}if(left->right != NULL) {if(left->right->max > left->max) {left->max = left->right->max;}}}/* * This function define the zig operation. * Parameters: *@cur: The current node to operate. * Returns: *None. */void zig(node* cur) {node* p = cur->parent;if(cur == p->left) {right_rotate(p);} else {left_rotate(p);}}/* * This function define the zig-zig operation. * Parameters: *@cur: The current node to operate. * Returns: *None. */void zig_zig(node* cur) {node *p = cur->parent;node *pp = p->parent;if(cur == p->left) {right_rotate(pp);right_rotate(p);} else {left_rotate(pp);left_rotate(p);}}/* * This function define the zig-zag operation. * Parameters: *@cur: The current node to operate. * Returns: *None. */void zig_zag(node* cur) {node *p = cur->parent;node *pp = p->parent;if(cur == p->left) {right_rotate(p);left_rotate(pp);} else {left_rotate(p);right_rotate(pp);}}/* * This function define the splay operation. * Parameters: *@cur: The current node to operate. *@dst: The destination node.  *  It's the parent node of @cur in final state.  * Returns: *None. */void splay(node* cur, node* dst) {while(cur->parent != dst) {node *p = cur->parent;if(p->parent == dst) {zig(cur);} else {node* pp = p->parent;if(cur == p->left && p == pp->left ||cur == p->right && p == pp->right) {zig_zig(cur);} else {zig_zag(cur);}}}}/* * This function find the preview node of the current node. * Parameters: *@cur: The current node to search. * Returns: *The preview node of the current node. */node* preview(node* cur) {node* pre = cur->left;if(pre != NULL) {while(pre->right != NULL) {pre = pre->right;}} else {pre = cur;while(pre != NULL) {if(pre->parent != NULL && pre == pre->parent->right) {pre = pre->parent;break;}pre = pre->parent;}}return pre;}/* * This function find the successor node of the current node. * Parameters: *@cur: The current node to search. * Returns: *The successor node. */node* successor(node *cur) {node *suc = cur->right;if(suc != NULL) {while(suc->left != NULL) {suc = suc->left;}} else {suc = cur;while(suc != NULL) {if(suc->parent != NULL && suc == suc->parent->left) {suc = suc->parent;break;}suc = suc->parent;}}return suc;}/* * This function insert a node into the splay tree. * Parameters: *@ins: The node to insert. * Returns: *The inserted node. */node* insert(node* ins) {node* p = root;node* pre = NULL;while(p != NULL) {pre = p;if(ins->tag == 1) {if(p->max < ins->value) {p->max = ins->value;}}if(ins->value > p->value) {p = p->right;} else if(ins->value < p->value) {p = p->left;} else {if(ins->tag == 1) {p->value = ins->value;p->max = p->value;p->tag = 1;}splay(p, NULL);free(ins);ins = p;return ins;}}if(pre != NULL) {if(pre->value > ins->value) {pre->left = ins;} else {pre->right = ins;}ins->parent = pre;} else {root = ins;}splay(ins, NULL);return ins;}/* * This function search the node according to the given range. * Parameters: * @s & @e: The left and right edge of range. * Returns: *The root of the sub-tree which contains nodes in the *given range. */node* search(int s, int e) {node* s_node = (node*) malloc(sizeof(node));s_node->value = s;s_node->max = 0;s_node->tag = 0;s_node->left = s_node->right = s_node->parent = NULL;node* e_node = (node*) malloc(sizeof(node));e_node->value = e;e_node->max = 0;e_node->tag = 0;e_node->left = e_node->right = e_node->parent = NULL;s_node = insert(s_node);e_node = insert(e_node);node* s_pre = preview(s_node);node* e_next = successor(e_node);splay(s_pre, NULL);splay(e_next, s_pre);return e_next->left;}/* * This function delete the nodes according to given range. * Parameters: *@s & @e: The left and right edge of range. * Returns: *None. */void remove(int s, int e) {node *del = search(s, e);if(del != NULL) {node* p = del->parent;if(p != NULL) {if(p->left == del) {p->left = NULL;} else {p->right = NULL;}} else {root = NULL;}while(p != NULL) {if(p->tag == 1) {p->max = p->value;} else {p->max = 0;}if(p->left != NULL) {if(p->max < p->left->max) {p->max = p->left->max;}}if(p->right != NULL) {if(p->max < p->right->max) {p->max = p->right->max;}}p = p->parent;}}}/* * The main program. */int main(void) {int n;scanf("%d", &n);node* min = (node*) malloc(sizeof(node));min->value = 0;min->max = 0;min->tag = 0;min->left = min->right = min->parent = NULL;node* max = (node*) malloc(sizeof(node));max->value = 1000000001;max->max = 0;max->tag = 0;max->left = max->right = max->parent = NULL;insert(min);insert(max);for(int i = 0; i < n; i++) {char op;getchar();scanf("%c", &op);if(op == 'I') {int k;scanf("%d", &k);node *ins = (node*) malloc(sizeof(node));ins->value = k;ins->max = k;ins->tag = 1;ins->left = ins->right = ins->parent = NULL;insert(ins);} else if(op == 'Q') {int k;scanf("%d", &k);node* r = search(1, k);printf("%d\n", r->max);} else if(op == 'D') {int a, b;scanf("%d %d", &a, &b);a = a > 1 ? a : 1;b = b < 1000000000 ? b : 1000000000;remove(a, b);}}return 0;}


聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.