白书中对treap的删除讲的不是很详尽,这里转载个补充(from:点击打开链接):
我们知道,二叉查找树相对来说比较容易形成最坏的链表情况,所以前辈们想尽了各种优化策略,包括AVL,红黑,以及今天
要讲的Treap树。
Treap树算是一种简单的优化策略,这名字大家也能猜到,树和堆的合体,其实原理比较简单,在树中维护一个"优先级“,”优先级“
采用随机数的方法,但是”优先级“必须满足根堆的性质,当然是“大根堆”或者“小根堆”都无所谓,比如下面的一棵树:
从树中我们可以看到:
①:节点中的key满足“二叉查找树”。
②:节点中的“优先级”满足小根堆。
节点里面定义了一个priority作为“堆定义”的旋转因子,因子采用“随机数“。
2:添加
首先我们知道各个节点的“优先级”是采用随机数的方法,那么就存在一个问题,当我们插入一个节点后,优先级不满足“堆定义"的
时候我们该怎么办,前辈说此时需要旋转,直到满足堆定义为止。
旋转有两种方式,如果大家玩转了AVL,那么对Treap中的旋转的理解轻而易举。
①: 左左情况旋转
从图中可以看出,当我们插入“节点12”的时候,此时“堆性质”遭到破坏,必须进行旋转,我们发现优先级是6<9,所以就要进行
左左情况旋转,最终也就形成了我们需要的结果。
②: 右右情况旋转
既然理解了”左左情况旋转“,右右情况也是同样的道理,优先级中发现“6<9",进行”右右旋转“最终达到我们要的效果。
3:删除
跟普通的二叉查找树一样,删除结点存在三种情况。
①:叶子结点
跟普通查找树一样,直接释放本节点即可。
②:单孩子结点
跟普通查找树一样操作。
③:满孩子结点
其实在treap中删除满孩子结点有两种方式。
第一种:跟普通的二叉查找树一样,找到“右子树”的最左结点(15),拷贝元素的值,但不拷贝元素的优先级,然后在右子树中
删除“结点15”即可,最终效果如下图。
第二种:将”结点下旋“,直到该节点不是”满孩子的情况“,该赋null的赋null,该将孩子结点顶上的就顶上,如下图:
4:总结
treap树在CURD中是期望的logN,由于我们加了”优先级“,所以会出现”链表“的情况几乎不存在,但是他的Add和Remove相比严格的
平衡二叉树有更少的旋转操作,可以说性能是在”普通二叉树“和”平衡二叉树“之间。
const int maxn=30000+1000;
int ch[maxn][2],val[maxn],counts[maxn],r[maxn],size[maxn],tot,root;
void Newnode(int &rt,int v)
{
rt=++tot;
val[rt]=v;
ch[rt][0]=ch[rt][1]=0;
counts[rt]=size[rt]=1;
r[rt]=rand();
}
inline void PushUp(int rt)
{
size[rt]=size[ch[rt][0]]+size[ch[rt][1]]+counts[rt];
}
void Rotate(int &x,int kind)
{
int y=ch[x][kind^1];
ch[x][kind^1]=ch[y][kind];
ch[y][kind]=x;
PushUp(x);PushUp(y);
x=y;
}
void Insert(int &rt,int v)
{
if(rt==0)
{
Newnode(rt,v);
return ;
}
if(v==val[rt]) counts[rt]++;
else
{
int kind=(v>val[rt]);
Insert(ch[rt][kind],v);
if(r[ch[rt][kind]]<r[rt])
Rotate(rt,kind^1);
}
PushUp(rt);
}
int select(int rt,int k)
{
if(size[ch[rt][0]]>=k) return select(ch[rt][0],k);
if(size[ch[rt][0]]+counts[rt]>=k) return val[rt];
return select(ch[rt][1],k-size[ch[rt][0]]-counts[rt]);
}
void remove(int &rt,int v)
{
if(val[rt]==v)
{
if(counts[rt]>1)
counts[rt]--;
else if(!ch[rt][0]&&!ch[rt][1])
{rt=0;return ;}
else
{
int kind=r[ch[rt][0]]<r[ch[rt][1]];
Rotate(rt,kind);
remove(rt,v);
}
}
else remove(ch[rt][v>val[rt]],v);
PushUp(rt);
}
void Init()
{
ch[0][0]=ch[0][1]=0;
size[0]=counts[0]=val[0]=0;
tot=root=0;
r[0]=(1LL<<31)-1;
Newnode(root,2000000001);
}
#define N 20005
struct Treap{
Treap *ch[2];
int r; //数字越大,优先级越高
int v; //值
int size; //子树的节点数
Treap(int v):v(v) { ch[0]=ch[1]=NULL; r=rand(); size=1;}
bool operator < (const Treap& rhs) const{
return r < rhs.r;
}
int cmp(int x) const{
if(x == v)return -1;
return x < v ? 0 : 1;
}
void maintain(){ //以该点为根的子树节点数(包括自己)
size = 1;
if(ch[0] != NULL) size += ch[0]->size;
if(ch[1] != NULL) size += ch[1]->size;
}
};
Treap* root[N];
void rotate(Treap* &o, int d){
Treap* k = o->ch[d^1]; o->ch[d^1] = k->ch[d]; k->ch[d] = o;
o->maintain(); k->maintain(); o = k;
}
void insert(Treap* &o, int x){
if(o == NULL) o = new Treap(x);
else {
int d = (x < o->v ? 0 : 1);
insert(o->ch[d], x);
if(o->ch[d] > o) rotate(o, d^1);
}
o->maintain();
}
void remove(Treap* &o, int x){
int d = o->cmp(x);
if(d == -1){
Treap* u = o;
if(o->ch[0] != NULL && o->ch[1] != NULL){
int d2 = (o->ch[0] > o->ch[1] ? 1: 0);
rotate(o, d2); remove(o->ch[d2], x);
}
else {
if(o->ch[0] == NULL) o = o->ch[1]; else o = o->ch[0];;
delete u;
}
}
else remove(o->ch[d], x);
if(o != NULL) o->maintain();
}
int kth(Treap* o, int k){
if(o == NULL || k<=0 || k> o->size) return 0;
int s = (o->ch[1] == NULL ? 0 : o->ch[1]->size);
if( k == s+1) return o->v;
else if(k <= s) return kth(o->ch[1], k);
else return kth(o->ch[0], k - s - 1);
}
void mergeto(Treap* &src, Treap* &dest){
if(src->ch[0] != NULL) mergeto(src->ch[0], dest);
if(src->ch[1] != NULL) mergeto(src->ch[1], dest);
insert(dest, src->v);
delete src;
src = NULL;
}
void removetree(Treap* &x){
if(x->ch[0] != NULL) removetree(x->ch[0]);
if(x->ch[1] != NULL) removetree(x->ch[1]);
delete x;
x = NULL;
}
void add_edge(int x){
int u = findset(from[x]), v = findset(to[x]);
if(u != v){
if(root[u]->size < root[v]->size) { fa[u] = v; mergeto(root[u], root[v]);}
else { fa[v] = u; mergeto(root[v], root[u]);}
}
}