Treap
Treap=Tree+Heap,Treap是一个二叉搜索树,它的左右子树都分别是一个Treap,和一般二叉排序树不同的是,Treap记录一个额外的数据(v),就是优先级(rank)。Treap在以关键字构成二叉排序树的同时,还满足堆的性质,不过Treap不一定是完全二叉树,而堆必须是完全二叉树.
1.结构体定义:
struct data{ int l,r; //左右子树 int v; //当前节点的值 int size; //由该节点为根构成的子树的大小 int rnd; //当前节点的优先级 int w; //和当前节点值相同的个数 }tr[MAXN]; |
2.Treap的性质:
(1) 如果b是a的左子树,那么tree[a].v>tree[b].v;
(2) 如果c是a的右子树,那么tree[a].v (3) 如果b,c分别是a的左右子树,那么tree[a].rank 3.Treap的旋转:跟普通的旋转一样 代码实现: void update(int k)//更新结点信息 { tr[k].size=tr[tr[k].l].size+tr[tr[k].r].size+tr[k].w; } void rturn(int &k) { int t=tr[k].l;tr[k].l=tr[t].r;tr[t].r=k; tr[t].size=tr[k].size;update(k);k=t; } void lturn(int &k) { int t=tr[k].r;tr[k].r=tr[t].l;tr[t].l=k; tr[t].size=tr[k].size;update(k);k=t; } 4.Treap的插入: (1)按照二叉树的插入方法,将结点插入到树中 (2)根据堆的性质(我们这里为最小堆)和优先级的大小调整结点位置。 代码实现: void insert(int &k,int x){ if(k==0){ size++;k=size; tr[k].size=tr[k].w=1;tr[k].v=x;tr[k].rnd=rand(); return; } tr[k].size++; if(tr[k].v==x)tr[k].w++; else if(x>tr[k].v){ insert(tr[k].r,x); if(tr[tr[k].r].rnd } else { insert(tr[k].l,x); if(tr[tr[k].l].rnd } } 5.Treap的删除: (1)利用二叉树的性质,找到和x值相同的节点 (2)如果和x值相同的节点的个数有多个,那么只需要将size和w减一即可 (3)如果和x值相同的节点的个数只有一个,而且其存在一个子结点,那么当前需要删除的结点的值应该更新为存在的子节点的值,如果存在两个子结点,那么判断两个子结点的优先级,优先级值小的被更新为删除结点的值。 代码实现: void del(int &k,int x){ if(k==0)return; if(tr[k].v==x){ if(tr[k].w>1){ tr[k].w--;tr[k].size--;return;//若不止相同值的个数有多个,删去一个 } if(tr[k].l*tr[k].r==0)k=tr[k].l+tr[k].r;//有一个儿子为空 else if(tr[tr[k].l].rnd rturn(k),del(k,x); else lturn(k),del(k,x); } else if(x>tr[k].v) tr[k].size--,del(tr[k].r,x); else tr[k].size--,del(tr[k].l,x); } 6.Treap的应用: (1) 查询x数的排名(若有多个相同的数,因输出最小的排名) 代码实现: int query_rank(int k,int x){ if(k==0)return 0; if(tr[k].v==x)return tr[tr[k].l].size+1; else if(x>tr[k].v) return tr[tr[k].l].size+tr[k].w+query_rank(tr[k].r,x); else return query_rank(tr[k].l,x); } (2) 查询排名为x的数 代码实现: int query_num(int k,int x){ if(k==0)return 0; if(x<=tr[tr[k].l].size) return query_num(tr[k].l,x); else if(x>tr[tr[k].l].size+tr[k].w) return query_num(tr[k].r,x-tr[tr[k].l].size-tr[k].w); else return tr[k].v; } (4) 求x的前驱(前驱定义为小于x,且最大的数)和求x的后继(后继定义为大于x,且最小的数) 代码实现: int ans=0; void query_pro(int k,int x){ //前驱 if(k==0)return; if(tr[k].v ans=k;query_pro(tr[k].r,x); } else query_pro(tr[k].l,x); } cout< void query_sub(int k,int x){ //后继 if(k==0)return; if(tr[k].v>x){ ans=k;query_sub(tr[k].l,x); } else query_sub(tr[k].r,x); } cout< 7.完整代码: struct data{ int l,r,v,size,rnd,w; }tr[MAXN]; int n,size,root,ans; void update(int k)//更新结点信息 { tr[k].size=tr[tr[k].l].size+tr[tr[k].r].size+tr[k].w; } void rturn(int &k) { int t=tr[k].l;tr[k].l=tr[t].r;tr[t].r=k; tr[t].size=tr[k].size;update(k);k=t; } void lturn(int &k) { int t=tr[k].r;tr[k].r=tr[t].l;tr[t].l=k; tr[t].size=tr[k].size;update(k);k=t; } void insert(int &k,int x) { if(k==0) { size++;k=size; tr[k].size=tr[k].w=1;tr[k].v=x;tr[k].rnd=rand(); return; } tr[k].size++; if(tr[k].v==x)tr[k].w++;//每个结点顺便记录下与该节点相同值的数的个数 else if(x>tr[k].v) { insert(tr[k].r,x); if(tr[tr[k].r].rnd } else { insert(tr[k].l,x); if(tr[tr[k].l].rnd } } void del(int &k,int x) { if(k==0)return; if(tr[k].v==x) { if(tr[k].w>1) { tr[k].w--;tr[k].size--;return;//若不止相同值的个数有多个,删去一个 } if(tr[k].l*tr[k].r==0)k=tr[k].l+tr[k].r;//有一个儿子为空 else if(tr[tr[k].l].rnd rturn(k),del(k,x); else lturn(k),del(k,x); } else if(x>tr[k].v) tr[k].size--,del(tr[k].r,x); else tr[k].size--,del(tr[k].l,x); } int query_rank(int k,int x) { if(k==0)return 0; if(tr[k].v==x)return tr[tr[k].l].size+1; else if(x>tr[k].v) return tr[tr[k].l].size+tr[k].w+query_rank(tr[k].r,x); else return query_rank(tr[k].l,x); } int query_num(int k,int x) { if(k==0)return 0; if(x<=tr[tr[k].l].size) return query_num(tr[k].l,x); else if(x>tr[tr[k].l].size+tr[k].w) return query_num(tr[k].r,x-tr[tr[k].l].size-tr[k].w); else return tr[k].v; } void query_pro(int k,int x) { if(k==0)return; if(tr[k].v { ans=k;query_pro(tr[k].r,x); } else query_pro(tr[k].l,x); } void query_sub(int k,int x) { if(k==0)return; if(tr[k].v>x) { ans=k;query_sub(tr[k].l,x); } else query_sub(tr[k].r,x); }