思路
Treap模板题
Treap
Treap是一种二叉查找树,并同时满足了二叉树和堆的性质。每次插入一个值的时候,如果我们新建了一个树上的点,我们给这个点赋一个随机值,然后通过旋转等操作来维护Treap堆的性质,保证了树高期望是O(logn),从而保证了时间复杂度。
通常来说Treap在常数大小和代码复杂度上要小于Splay。
节点定义
常规情况下Treap的节点需要储存左右儿子,子树大小,节点中相同元素的数量,随机数的值,我因为在treap结构体中定义了一个nexts的二维数组,所以node中没有写lson,rson。1
2
3
4
5template <typename T>
struct node{
T val;
int siz,rnd,cnt;
};
节点信息更新
更新最基本的子树大小以及一些其他内容,时间复杂度O(1)1
2
3void push_up(int k){
tree[k].siz=tree[nexts[k][0]].siz+tree[nexts[k][1]].siz+tree[k].cnt;
}
旋转
与常见二叉树旋转类似,见参考中的详细解释
插入
从根开始,比根的val小进入左儿子,不然进入右儿子,如果值相等重复计数器cnt++。如果该节点尚未被创建,则初始化一个新节点放到当前位置。时间复杂度O(logn)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17void ins(int &p,int x){
if(p==0){
tree[nodecnt].siz=tree[nodecnt].cnt=1,tree[nodecnt].val=x,tree[nodecnt].rnd=rand();
p=nodecnt++;
return;
}
tree[p].siz++;
if(tree[p].val==x)tree[p].cnt++;
else if(x>tree[p].val){
ins(nexts[p][1],x);
if(tree[nexts[p][1]].rnd<tree[p].rnd)rotation(p,0);
}
else{
ins(nexts[p][0],x);
if(tree[nexts[p][0]].rnd<tree[p].rnd)rotation(p,1);
}
}
删除
如果删除值小于当前值,进入左子树,大于进入右子树
当等于时:
如果出现次数cnt大于1,cnt—
不然我们就要删除该节点,并分三种情况讨论:
- 没有左和右儿子,直接删除
- 有左或右儿子,用该儿子替换该节点
- 有左和右儿子,旋转并进入当前节点旋转后的位置,直到变成2的情况
时间复杂度O(logn)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26void del(int &p,int x){
//since through the data of bzoj we can find that x may not exist
if(p==0)return;
if(tree[p].val==x){
if(tree[p].cnt>1)tree[p].cnt--,tree[p].siz--;
else{
if(nexts[p][0]==0||nexts[p][1]==0)p=nexts[p][0]+nexts[p][1];
else if(tree[nexts[p][0]].rnd<tree[nexts[p][1]].rnd){
rotation(p,1);
del(p,x);
}
else{
rotation(p,0);
del(p,x);
}
}
}
else if(tree[p].val<x){
tree[p].siz--;
del(nexts[p][1],x);
}
else{
tree[p].siz--;
del(nexts[p][0],x);
}
}
查询数字x的排名
递归查询,并记录小于这个值的节点数量,时间复杂度O(logn)1
2
3
4
5
6int findRank(int p,int x){
if(p==0)return 0;
if(tree[p].val==x)return tree[nexts[p][0]].siz+1;
else if(tree[p].val<x)return tree[nexts[p][0]].siz+tree[p].cnt+findRank(nexts[p][1],x);
else return findRank(nexts[p][0],x);
}
查询排名为x的数
与查排名思路相同1
2
3
4
5
6T findKey(int p,int x){
if(p==0)return 0;
if(tree[nexts[p][0]].siz>=x)return findKey(nexts[p][0],x);
else if(tree[nexts[p][0]].siz+tree[p].cnt<x)return findKey(nexts[p][1],x-tree[nexts[p][0]].siz-tree[p].cnt);
else return tree[p].val;
}
查找前驱和后继
相同思路1
2
3
4
5
6
7
8
9
10
11T findPre(int p,int x){
if(p==0)return -INF;
if(tree[p].val<x)return max(tree[p].val,findPre(nexts[p][1],x));
else return findPre(nexts[p][0],x);
}
//find the minimum number that larger than x
T findNext(int p,int x){
if(p==0)return INF;
if(tree[p].val>x)return min(tree[p].val,findNext(nexts[p][0],x));
else return findNext(nexts[p][1],x);
}
参考
代码
1 |
|