Treap有很多写法,今天研究了一下,觉得这种写法比较好(主要是代码比原来短多了):
如果是直接编的话,会比较烦,因为很多代码是对称的(对于旋转,插入,删除这些操作来说).
所以我们可以用0表示左,1表示右.那么son[0]是左儿子,son[1]是右儿子;Rotate(p,0)表示;左旋Rotate(p,1)表示右旋.
这样的话,很多对称的操作,都可以通过传递一个bool参数统一起来,就不用写两个对称的代码了.
定义:
struct Node
{
Node *son[2]; //用指针写会比较方便
int Key,w,Size; //Key是关键字,w的随机生成的修正值,Size是子树大小
Node(int a,int b,int c,Node *d):Key(a),w(b),Size(c)
{
son[0]=son[1]=d;
}
}*__root,*null;
其中,__root是根结点,null是哨兵,空节点都指向哨兵.当然哨兵要初始化.
认真分析旋转,插入,删除操作之后会发现,哨兵的作用很大,很多判断都简化了.
其实可以加个优化,就是把重复的结点合并(在大多数题目上都可以这样做).
初始化:
void init()
{
null=new Node(-oo,oo,0,0); //这里维护的是小根堆
__root=null=null->son[0]=null->son[1]=null;
}
核心操作–旋转:
先来看看原始的左旋和右旋:
void Rotate_left(Node *&p)
{
Node *u=p->son[1];
p->son[1]=u->son[0];
u->son[0]=p;
u->Size=p->Size;
p->Size=p->son[0]->Size+p->son[1]->Size+1;
p=u;
}
void Rotate_right(Node *&p)
{
Node *u=p->son[0];
p->son[0]=u->son[1];
u->son[1]=p;
u->Size=p->Size;
p->Size=p->son[0]->Size+p->son[1]->Size+1;
p=u;
}
然后发现,它们的差异,只不过是前三句代码的下标而已.所以,果断用bool替代.
写inline会比较快:
inline void Rotate(Node *&p,bool b) //Rotate(p,0)表示;左旋Rotate(p,1)表示右旋
{
Node *u=p->son[!b];
p->son[!b]=u->son[b];
u->son[b]=p;
u->Size=p->Size;
p->Size=p->son[0]->Size+p->son[1]->Size+1;
p=u;
}
插入:
void Insert(Node *&p,const int &x) //注意引用
{
if (p==null) //如果找到了位置就插入
{
p=new Node(x,rand(),1,null);
return;
}
bool b=(x>p->Key); //判断是往左子树还是右子树插入
Insert(p->son[b],x);
p->Size++; //维护Size
if (p->son[b]->w<p->w) Rotate(p,!b); //在左子树就右旋,在右子树就左旋
}
删除:
void Delete(Node *&p,const int &x)
{
if (p->son[0]==null && p->son[1]==null)
{
p=null;//这里是等到目标结点变为叶子才删除,仅比常规的删除多做了一次旋转,但代码短多了
return;
}
bool b; //这里的b为if语句后的操作提供了便利
if (p->Key==x)
{
b=(p->son[1]->w>p->son[0]->w);
Rotate(p,b);
} else b=(x>p->Key);
p->Size--;
Delete(p->son[b],x);
}
认真观察之后,可以发现这种删除可以写成非递归的,但由于引用p比较麻烦,所以就干脆写递归了.
下面的是次要的操作,基本上就和原来的一样了,不过也贴贴代码吧.
因为这些操作都可以写成非递归的,所以就非递归了(非递归比较快).
int Find(int x) //返回第x小的元素
{
Node *p=__root;
for (; p->son[0]->Size+1!=x; )
if (p->son[0]->Size<x)
{
x-=p->son[0]->Size+1;
p=p->son[1];
} else p=p->son[0];
return p->Key;
}
int Rank(int x) //返回x的排名(第几小)
{
Node *p=__root;
int Count=0;
for (; p!=null; )
if (p->Key<x)
{
Count+=p->son[0]->Size+1;
p=p->son[1];
} else p=p->son[0];
return Count+1;
}
int Pred(int x) //返回x的前驱(这里默认x有前驱)
{
Node *p=__root;
int ans;
for (; p!=null; )
if (p->Key<x)
{
ans=p->Key;
p=p->son[1];
} else p=p->son[0];
return ans;
}
int Succ(int x) //返回x的后继(这里默认x有后继)
{
Node *p=__root;
int ans;
for (; p!=null; )
if (p->Key>x)
{
ans=p->Key;
p=p->son[0];
} else p=p->son[1];
return ans;
}