Treap是什么?Treap = Tree + Heap !
我们知道,在BST中,插入有序数据会导致BST退化成一条链(有些极限数据就是这样)。那么我们要怎么办呢?
其实其他大神们在此之前已经注意到了这个问题,所以他们就研发了一种新的东西,就是在BST的基础上,给每一个节点额外地加上了一个fix
值(中文为修正值),然后这棵树除了要满足BST的性质之外,还要符合堆的性质(堆的值是fix)(这里是最小堆)。所以就有了一个新的东西:Treap。
这样,Treap的形态就不仅仅由value
控制,而且还由fix
控制。我们只要保证fix
足够随机,这棵树就不会轻易地退化成一条链,平均(理想)深度为$\log_n$。
那么不是每次操作都是刚好能同时符合两个性质的。有时候我们改变了一些节点,使它满足BST的性质,但是却破坏了堆的性质,怎么办呢?
因为此时已经满足了BST性质,所以可以在不破坏BST性质的前提下,使它满足堆的性质。
所以Treap的发明者又弄出来了两个东西:左旋、右旋。就是用来修复堆的性质的。
举个栗子,像下面的图
如果B.fix > A.fix,那么就不符合最小堆的性质,那么我们可以对B点进行左旋操作,就是把B改成A的右孩,B的左孩改成β。讲得太抽象了?没事,下面有动画!
所以右旋的代码是这个样子滴:
void leftRotate(NODE *&p) {
NODE *t = p->rght;
p->rght = t->left;
t->left = p;
p = t;
t = p->left;
}
举个栗子,还是下面的图
如果A.fix
> B.fix
,那么就不符合最小堆的性质,那么我们可以对A点进行左旋操作,就是把A改成B的左孩,A的右孩改成β。讲得太抽象了?没事,下面还是有动画!
所以左旋的代码是这个样子滴:
void rghtRotate(NODE *&p) {
NODE *t = p->left;
p->left = t->rght;
t->rght = p;
p = t;
t = p->rght;
}
一句话,旋转就是一种不会破坏BST性质还会修复堆性质的旋转
算了,还是证明一下比较好,证明不会破坏BST性质:
首先,以左旋为例,旋转前,value[A]
<value[β]
<value[B]
(α和γ没有变,所以可以不讨论);旋转后,依然还是value[A]
<value[β]
<value[B]
,所以不会破坏BST性质。
因为这棵树也是一棵BST树,所以这些操作跟BST的完全相同!
遍历:
void print(const NODE *p) {
if (p) {
printf("(");
print(p -> left);
printf(" %d ", p -> value);
print(p -> rght);
printf(")");
}
}
查找:
NODE* find(NODE *p, const int value) {
if (!p) return NULL;
else if (p->value == value) return p;
else if (p->value > value) return find(p->left, value);
else return find(p->rght, value);
}
插入的标准操作就是这样:
太烦了?一句话概括,就是在底端合适的位置先插入,然后再用左右旋把这个节点一步一步地旋转上去。
按照上面的操作,可得代码:
void insert(NODE *&p, const int value) {
if (!p) {
p = new NODE();
p->fix = rand();
p->value = value;
}
else if (value < p->value) {
insert(p->left, value);
if (p->left->fix < p->fix)
rghtRotate(p);
}
else {
insert(p->rght, value);
if (p->rght->fix < p->fix)
leftRotate(p);
}
}
如果这个点至多有一个孩子(一个或没有),那么这个节点肯定是可以被立刻删除的。(可以用孩子代替该点的位置)
但是如果有两个孩子那怎么办呢?我们可以通过旋转使这个节点变成可以立刻删除的点。如果该节点的左孩的修正值小于右孩的修正值,右旋该节点,否则左旋该节点。直到只有一个孩子就可以删除了。
所以删除的代码如下:
void remove(NODE *&p, const int value) {
if (!p) return;
if (value == p->value) {
if (!p->rght || !p->left) {
NODE *t = p;
if (!p->rght) p = p->left;
else p = p->rght;
delete t;
}
else {
if (p->left->fix < p->rght->fix) {
rghtRotate(p);
remove(p->rght, value);
}
else {
leftRotate(p);
remove(p->left, value);
}
}
}
else if (value < p->value) remove(p->left, value);
else remove(p->rght, value);
}
因为这是一棵BST树,而且在Treap算法的优化下能优化到平均深度为$\log_n$,所以查找、插入、删除的时间复杂度是$O(\log_n)$。
CC 原创文章采用CC BY-NC-SA 4.0协议进行许可,转载请注明:“转载自:Treap”