Treap是拥有键值和优先级两种权值的树
对于键值而言它是排序二叉树
对于优先级而言它是堆
struct Node {
Node *ch[2]; //左右子树
int r; //优先级。数值越大,优先级越高
int v; //值
int cmp(int x) const {
if(x == v) return -1;
return x < v ? 0 : 1;
}
};
旋转操作
d=0 代表左旋,d=1 代表右旋
技巧性较强,d^1等价于1-d(因为d就是0或1)
o是指向一个Treap节点的指针,本身可以修改所以是引用
void rotate(Node* &o, int d) {
Node *k = o->ch[d^1];
o->ch[d^1] = k->ch[d];
k->ch[d] = o;
o = k;
}
插入操作
插入节点时,首先随机给新节点一个优先级,然后执行普通的插入算法
执行完毕后用左右旋让这个节点往上走,从而维持堆性质
//在以o为根的子树中插入键值x,修改o
void insert(Node* &o, int x) {
if(o == NULL){
o = new Node();
o->ch[0] = o->ch[1] = NULL;
o->v = x;
o->r = rand();
}
else {
int d = o->cmp(x);
insert(o->ch[d], x);
if(o->ch[d] ->r > o->r) rotate(o, d^1);
}
}
删除操作
删除时,首先找到待删除节点。
如果它只有一棵子树,情况就简单了。
直接用这棵子树代替待删除节点成为根即可(o为叶子也符合这个情况)
有两棵子树时,先把优先级高的旋转到根,然后递归在另一颗子树中删除节点o
void remove(Node* &o, int x){
int d = o->cmp(x);
if(d == -1) {
if(o->ch[0] == NULL) o = o->ch[1];
else if(o->ch[1] == NULL) o = o->ch[0];
else {
int d2 = (o->ch[0] ->r > o->ch[1] ->r ? 1 : 0);
rotate(o, d2); //左子树优先级大右旋,右子树优先级大左旋
remove(o->ch[d2], x);
}
}
else
remove(o->ch[d], x);
}
查找操作
删除和插入前都需要查找,防止“待插入的值已经存在”和“待删除的值不存在”
int find(Node* &o, int x){
while(o != NULL){
int d = o->cmp(x);
if(d == -1) return 1; //存在
else o = o->ch[d];
}
return 0; //不存在
}