平衡树
Treap
Treap 是既满足二叉搜索树的性质又满足堆性质的树。它通过随机优先级实现树高在 \(O(\log(n))\) 级别。以下是两种实现 Treap 的方式。
旋转 Treap
非旋 Treap
又称 FHQ-Treap。
原理:将要修改的部分分裂出来,修改,然后合并。
分裂
void split(int k, int id, int &p, int &q){
if(!id){
p = q = 0;
return;
}
pd(id);
if(tr[tr[id].ls].sz >= k){ //左子树在p中
split(k, tr[id].ls, p, tr[id].ls);
q = id;
}
else{ //右子树在q中
split(k - tr[tr[id].ls].sz - 1, tr[id].rs, tr[id].rs, q);
p = id;
}
pp(id);
}
合并
int merge(int p, int q){
if(!p || !q)
return p + q;
if(tr[p].p > tr[q].p){ //p当根
pd(p);
tr[p].rs = merge(tr[p].rs, q);
pp(p);
return p;
}
else{ //q当根
pd(q);
tr[q].ls = merge(p, tr[q].ls);
pp(q);
return q;
}
}
splay
splay 有一字旋和之字旋。
可以证明,其时间复杂度是 \(O(\log_2 n)\)。
旋转函数
void rotate(int x){
int y = tr[x].fa, z = tr[y].fa, k = (tr[y].s[1] == x);
pd(x);
pd(y);
tr[z].s[tr[z].s[1] == y] = x, tr[x].fa = z;
tr[y].s[k] = tr[x].s[k ^ 1], tr[tr[x].s[k ^ 1]].fa = y;
tr[x].s[k ^ 1] = y, tr[y].fa = x;
pp(y);
pp(x);
}
核心函数
void splay(int x, int k){ //把x旋转到k下面
while(tr[x].fa != k){
int y = tr[x].fa, z = tr[y].fa;
if(z != k){
if((tr[y].s[1] == x) ^ (tr[z].s[1] == y))
rotate(x);
else
rotate(y);
}
rotate(x);
}
if(!k)
root = x;
}
易错点
1 一个点的 \(sz\) 要设为 \(1\), pushup 时要 \(sz\) 时左右孩子之和加 \(1\)。