關於平衡樹(Treap)

         平衡樹是什麼?

  其實平衡樹就是支持旋轉二叉查找樹ios

 

 


 

   什麼是二叉查找樹呢?

  其實就是知足(左子樹(所有節點) < 根 < 右子樹(所有節點))的一棵樹,好比↓git

 

(注意並非每一個節點都要是滿的,也就是說它不必定是一棵徹底二叉樹)dom

  那麼二叉查找樹有什麼用呢?url

 假如咱們要查詢第k大的數,咱們從根節點開始往下走,假如左子樹大小大於等於k,咱們就進入左子樹去找;若是左子樹大小等於(k-1),就輸出當前節點;假如左子樹大小小於(k-1),咱們就進入右子樹找排名爲(k-(左子樹大小+1))的數。spa

  這樣因爲樹的理想高度爲logn,因此單次查找複雜度爲O(logn)..net

  同理,插入、刪除、查詢前驅後繼等操做的複雜度都爲O(logn).code

    但是,二叉查找樹並不能處理特殊狀況,好比一個遞增數列,那麼若是你一個一個插入的話二叉查找樹就退化成了一條,不具有任何優越性了。blog

    因此有前輩發明了Treap.get

 

 


 

 

    Treap是一個同時具備二叉查找樹與堆的性質的樹形結構(堆的本質也是二叉樹)。好比↓string

 

   具體的實現過程就是咱們在新加入一個節點時賦給他自己鍵值的同時給他一個優先級,而這個優先級隨機產生,那就不怕被特殊數據卡了對不對!

    那咱們怎麼維護堆的性質呢?

    ——旋轉。

 

   假如左圖中x的優先級大於u,那就要經過右旋把x旋轉到根的位置,同時爲了維護二叉查找樹的性質,因此要把x的右兒子接到u上。

(本身舉個例子畫畫圖就明白了)。

    旋轉代碼:

inline void rotate(Node* &cur,int d) { Node *k = cur->ro[d^1] ; cur->ro[d^1] = k->ro[d] ; k->ro[d] = cur ; cur->pushup() ; k->pushup() ; cur = k ; }

(ro[0],ro[1]分別表明左右兒子,當d等於0時表示左旋,等於1時表示右旋)

 


 

   給出模板題與參考代碼:(我建的是大根堆)

 https://www.luogu.org/problemnew/show/P3369

#include<iostream> #include<cstdio> #include<cstring>
using namespace std; const int INF = (1<<29) ; inline int read() { int k = 0 , f = 1 ; char c = getchar() ; for( ; !isdigit(c) ; c = getchar()) if(c == '-') f = -1 ; for( ; isdigit(c) ; c = getchar()) k = k*10 + c-'0' ; return k*f ; } inline int randomm() { static int seed = 707 ; return seed = seed*3214567%23456789 ; } struct Node { int key, siz, rap ; Node *ro[2] ; Node(int x) { siz = 1 ; key = x ; rap = randomm() ; ro[1] = ro[0] = NULL ; } inline int pushup() { siz = 1 ; if(ro[0]) siz += ro[0]->siz ; if(ro[1]) siz += ro[1]->siz ; } inline int cop(int x) { if(x == key) return -1 ; return x < key ? 0 : 1 ; } }; int n, x, k, accm, ans ; inline void rotate(Node* &cur,int d) { Node *k = cur->ro[d^1] ; cur->ro[d^1] = k->ro[d] ; k->ro[d] = cur ; cur->pushup() ; k->pushup() ; cur = k ; } void insert(Node* &cur) { if(!cur) { cur = new Node(x) ; return ; } int d = x < cur->key ? 0 : 1 ; insert(cur->ro[d]) ; if(cur->ro[d]->rap > cur->rap) rotate(cur,d^1) ; else cur->pushup() ; } void Delete(Node* &cur) { int d = cur->cop(x) ; if(d == -1) { if(!cur->ro[0]) cur = cur->ro[1] ; else if(!cur->ro[1]) cur = cur->ro[0] ; else { int dd = cur->ro[0]->rap < cur->ro[1]->rap ? 0 : 1 ; rotate(cur,dd) ; Delete(cur->ro[dd]) ; } } else Delete(cur->ro[d]) ; if(cur) cur->pushup() ; } void rank(Node *cur) { if(!cur) return ; if(x < cur->key) { rank(cur->ro[0]) ; return ; } int lsiz = cur->ro[0] == NULL ? 0 : cur->ro[0]->siz ; if(x == cur->key) { ans = min(ans,accm+lsiz+1) ; rank(cur->ro[0]) ; } else { accm += (lsiz+1) ; rank(cur->ro[1]) ; } } void kth(Node *cur) { int cm = 1 ; if(cur->ro[0]) cm += cur->ro[0]->siz ; if(cm == k) { ans = cur->key ; } else if(k < cm) { kth(cur->ro[0]) ; } else { k -= cm ; kth(cur->ro[1]) ; } } void pre(Node *cur) { if(!cur) return ; if(cur->key < x) { ans = max(ans,cur->key) ; pre(cur->ro[1]) ; } else pre(cur->ro[0]) ; } void sub(Node *cur) { if(!cur) return ; if(cur->key > x) { ans = min(ans,cur->key) ; sub(cur->ro[0]) ; } else sub(cur->ro[1]) ; } int main() { n = read() ; Node *root = NULL ; int opt ; while(n--) { opt = read() ; switch(opt) { case 1:x = read(), insert(root) ; break ; case 2:x = read(), Delete(root) ; break ; case 3:x = read(), accm = 0, ans = INF, rank(root), printf("%d\n",ans) ; break ; case 4:k = read(), kth(root), printf("%d\n",ans) ; break ; case 5:x = read(), ans = -INF, pre(root), printf("%d\n",ans) ; break ; case 6:x = read(), ans = INF, sub(root), printf("%d\n",ans) ; break ; } } return 0 ; }

 


 

 

  再給出一道基礎應用題:

https://www.luogu.org/problemnew/show/P1503

 (未完待續)

相關文章
相關標籤/搜索