伸展树(Splay Tree),也叫分裂树,是一种二叉排序树,它能在O(log n)内完成插入、查找和删除操作。
旋转操作(左旋右旋)和前面的treap一样,伸展树最重要的操作是伸展操作,它把一个指定结点x自底向上旋转到根结点。分三种情况:
struct Node{ Node* ch[2]; int s; int v; int flip; int cmp(int k) const{ int d=k-ch[0]->s; if(d==1) return -1; return d<=0?0:1; } void maintain(){ s=ch[0]->s+ch[1]->s+1; } };
伸展操作,利用刚才那三种操作,把第k个元素旋转到根。
//k>=1 void splay(Node*& o,int k){ o->pushdown(); int d=o->cmp(k); if(d==1) k-=o->ch[0]->s+1; if(d!=-1){ Node* p=o->ch[d]; p->pushdown(); int d2=p->cmp(k); int k2=(d2==0?k:k-p->ch[0]->s-1); if(d2!=-1){ splay(p->ch[d2],k2); if(d2==d) rotate(o,d^1); else rotate(o->ch[d],d); } rotate(o,d^1); } }
Node* merge(Node* left,Node* right){ splay(left,left->s); left->ch[1]=right; left->maintain(); return left; }
void split(Node* o,int k,Node*& left,Node*& right){ splay(o,k); left=o; right=o->ch[1]; o->ch[1]=null; left->maintain(); }
range(L,R),截出[L,R)这一段放在root->ch[1]->ch[0],[L,R)是加了一个起始虚拟结点之后的区间。
Node*& range(int L,int R){ splay(root,L); splay(root->ch[1],R-L+1); return root->ch[1]->ch[0]; }
由于split左边不能为空,因此解决问题时通常加一个开始结点和一个结束结点。
伸展树用于处理序列问题,插入删除旋转之类的问题。