平衡树是什么?
其实平衡树就是支持旋转的二叉查找树。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
(未完待续)