1、基本性质
tree+heap=treap
平衡树包含treap 红黑树 splay sbt AVL等等
splay比较常用
treap=
①BST 二叉搜索树
+
②heap
2、set不能做的操作
⑤和⑥这种与排名相关的操作比较困难
3、treap的实现
思想:让二叉搜索树尽量变得随机(以大根堆为例)
key是搜索树的值,val是大根堆的值
使一棵树同时满足是二叉搜索树,同时还要满足是大根堆
注意:平衡树的初始状态一般会加两个哨兵,正无穷和负无穷,防止边界问题
左旋和右旋操作:为了交换节点
性质:旋转后,不会影响中序遍历的顺序。
插入操作:按二叉搜索树的性质插入点后,给val赋值,并且如果val不满足大根堆,则用旋转操作转上去(同时还可以保证平衡树)
删除操作:旋转到叶节点,然后删除。注意还要维护堆的性质,故哪个子树的val更大就和哪边旋转。
模板(例题):https://www.acwing.com/problem/content/255/
#include<bits/stdc++.h> #define fore(x,y,z) for(LL x=(y);x<=(z);x++) #define forn(x,y,z) for(LL x=(y);x<(z);x++) #define rofe(x,y,z) for(LL x=(y);x>=(z);x--) #define rofn(x,y,z) for(LL x=(y);x>(z);x--) #define pub push_back #define all(x) (x).begin(),(x).end() #define fi first #define se second using namespace std; typedef long long LL; typedef pair<int,int> PII; typedef pair<LL,LL> PLL; const int N=100010; int INF=0x3f3f3f3f; int n; struct Node { int l,r; int key,val; int cnt,sz; }tr[N]; int root,idx; int GetNode(int key)//创建节点 { tr[++idx].key=key; tr[idx].val=rand(); tr[idx].cnt=tr[idx].sz=1; return idx; } void PushUp(int p) { tr[p].sz=tr[tr[p].l].sz+tr[tr[p].r].sz+tr[p].cnt; } void Build() { GetNode(-INF); GetNode(INF); root=1; tr[1].r=2; PushUp(root); } void zig(int &p)//右旋 //输入为引用的原因是,旋转会使当前位置的节点变化,希望将变化传出去 { int q=tr[p].l; tr[p].l=tr[q].r; tr[q].r=p; p=q; PushUp(tr[p].r); PushUp(p); } void zag(int &p)//左旋 { int q=tr[p].r; tr[p].r=tr[q].l; tr[q].l=p; p=q; PushUp(tr[p].l); PushUp(p); } void Insert(int &p,int key) //由于需要满足堆的性质,故需要在函数里对p进行旋转,故传引用 { if(!p) p=GetNode(key); else if(tr[p].key==key) tr[p].cnt++; else if(tr[p].key>key) { Insert(tr[p].l,key); if(tr[tr[p].l].val>tr[p].val)//递归修改 { zig(p); } } else { Insert(tr[p].r,key); if(tr[tr[p].r].val>tr[p].val)//递归修改 { zag(p); } } PushUp(p); } void Remove(int &p,int key) { if(!p) return; if(tr[p].key==key) { if(tr[p].cnt>1) tr[p].cnt--; else if(tr[p].l||tr[p].r)//不是叶子节点 { //根据左右的存在性以及val的大小判断左旋还是右旋 if(tr[p].r==0||tr[tr[p].l].val>tr[tr[p].r].val) { zig(p); Remove(tr[p].r,key); } else { zag(p); Remove(tr[p].l,key); } } else { p=0;//叶子节点直接删除 } } else { if(tr[p].key>key) { Remove(tr[p].l,key); } else { Remove(tr[p].r,key); } } PushUp(p); } int GetRank(int p,int key)//返回在p为根的子树中的排名 { if(!p) return 0; if(tr[p].key==key) { return tr[tr[p].l].sz+1; } else if(tr[p].key>key) { return GetRank(tr[p].l,key); } else { return GetRank(tr[p].r,key)+tr[p].cnt+tr[tr[p].l].sz; } } int GetKey(int p,int rank)//返回在p为根的子树中排名为rank的key { if(!p) return INF; if(tr[tr[p].l].sz>=rank) return GetKey(tr[p].l,rank); if(tr[tr[p].l].sz>=rank) return GetKey(tr[p].l,rank); if(tr[tr[p].l].sz+tr[p].cnt>=rank) return tr[p].key; else return GetKey(tr[p].r,rank-(tr[tr[p].l].sz+tr[p].cnt)); } int GetPrev(int p,int key)//严格小于key的最大数 { if(!p) return -INF; if(tr[p].key>=key) { return GetPrev(tr[p].l,key); } else { return max(tr[p].key,GetPrev(tr[p].r,key)); //max的原因是,GetPrev没找到返回-INF,找到了则比tr[u].key大 } } int GetNext(int p,int key) { if(!p) return INF; if(tr[p].key<=key) { return GetNext(tr[p].r,key); } else { return min(tr[p].key,GetNext(tr[p].l,key)); //与GetPrev对称 } } void YD() { Build(); cin>>n; while(n--) { int op;int x; cin>>op>>x; if(op==1) { Insert(root,x); } if(op==2) { Remove(root,x); } if(op==3) { cout<<GetRank(root,x)-1<<endl; } if(op==4) { cout<<GetKey(root,x+1)<<endl; } if(op==5) { cout<<GetPrev(root,x)<<endl; } if(op==6) { cout<<GetNext(root,x)<<endl; } } } int main() { ios_base::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr); int T=1; //cin >> T; while (T--) { YD(); } return 0; }View Code
标签:return,val,int,tr,else,treap,算法,key,AcWing From: https://www.cnblogs.com/ydUESTC/p/16739396.html