作为蒟蒻的我一直觉得平衡树很高端,便从treap学起。。。。【为了省选?】
treap , 顾名思义 tree + heap....
为了学它我先写了一份普通的二叉排序树(定义就不说了)
#include<cstdio>
#include<string>
#include<cstring>
#include<iostream>
#include<cstdlib>
#include<algorithm>
const int N = 5e5+7;
int n,a[N];
int Ran () {
return (rand () << 16) + (rand () << 7) + rand();
}
int ran () {
return Ran () % rand() + Ran () % rand() + Ran() % rand();
}
template <class T>
class Treap {
private :
struct Node {
Node *l,*r;
int size,hr;
T data;
Node () {}
Node (T data) : data(data) { size = 0; hr = ran(); }
}*root,meme[N],*pool = meme;
public :
Treap () { } // 一定要先clear
void clear () {
root = NULL; pool = meme;// root = new (pool++) Node();
}
void Insert (T ball) {
Node *now = root;
while (now) {
if(now -> data < ball) if(now -> r) now = now -> r; else {
now -> r = new (pool++) Node (ball); return;
} else if(now -> l) now = now -> l; else {
now -> l = new (pool++) Node (ball); return;
}
}
root = new (pool++) Node (ball);
}
void Print_Dfs (Node *now) {
if(!now) return;
Print_Dfs(now->l);
printf("%d\n",now->data);
Print_Dfs(now->r);
}
void Print() { Print_Dfs(root); }
};
Treap<int>treap;
int main () {
scanf("%d",&n);
for(int i=1;i<=n;++i) scanf("%d",&a[i]),treap.Insert(a[i]);
treap.Print();
return 0;
}
为了检验正确性,在codevs上交了一发排序,a了。。。、
接下来看treap,基友们都说它是最好写的平衡树。
它是怎样维护平衡的呢?给每一个节点赋一个随机的值,在原节点值满足二叉排序树的性质的同时,所有节点的随机值满足堆的性质(不一定是完全二叉树,这里的堆的性质是指左右子树的任意节点的随机值都大于本节点的随机值(或小于))。
在按二叉排序树的方式插入节点后,可能会破坏堆的性质,这时我们就需要旋转操作来维护他了...
我们首先要想这样一个事情,当新的节点向一个节点的右边插入时,只可能破坏右子树的堆的性质,因为左子树在插入前满足堆的性质,而插入时不关它鸟事(原谅我的粗鄙)
所以还满足堆的性质,在向右边插入时也是一个道理,
在二叉堆中我们常常用 swap 一下 的方法就维护完成了,而这里显然不行,因为我们还要维护排序二叉树的性质,所以就不得不用旋转,怎么旋呢?
左旋:
现在主要节点是P,而新的节点往右边插入后,它的右儿子R破坏了堆的性质,在堆中,它们交换了一下,父子关系就倒过来了。
观察上面两个图,可以发现,它们的父子关系也倒了过来并仍满足排序二叉树和堆的性质
我现在上几张旋转的过程,代码细节就参照我的吧。。。
图画的真的很辣鸡。。。。
删除操作等等以后再说
That is all. Thank you for watching!