学习笔记 Treap
2013-10-13 09:32
204 查看
学习Treap之前先学的Splay,然后发现Treap真是太简单了,Greatwall1995神犇的意见要听啊
BST的缺陷是因为人为搞单调数据才爆的,随机数据还是logn的时间,Treap的思想也基于此,给每个添加的点赋一个随机优先值,然后整个树关于优先值是一个堆。
CLRS上要求读者证明插入节点后期望的旋转次数小于2,看到之后就吓呆了
接下来是具体实现,lrj的代码实在是太漂亮了,忍不住贴出来
const int maxn=1000;
double random(){ return (double)rand()/RAND_MAX;}
int random(int m) { return (int)((m-1)*random()+0.5);}
struct node{
node* ch[2];
int v,r,s;
node (int v=0):v(v) {ch[0]=ch[1]=NULL;s=1;r=random(maxn);}
int cmp(int x)const{
if (v==x)return -1;
return x<v?0:1;
}
void maintain(){
s=1;
if (ch[0]!=NULL)s+=ch[0]->s;
if (ch[1]!=NULL)s+=ch[1]->s;
}
};
node* root;
void rotate(node* & o,int d){
node* k=o->ch[d^1]; o->ch[d^1]=k->ch[d]; k->ch[d]=o;
o->maintain(); k->maintain(); o=k;
}
node* find1(node* o,int x){
if (o==NULL)return NULL;
if (o->v==x)return o;
return o->v<x? find1(o->ch[1],x):find1(o->ch[0],x);
}
void insert(node* &o,int x){
if (o==NULL){o=new node(x);}
else {
int d=o->cmp(x);
insert(o->ch[d],x);if (o->ch[d]->r>o->r)rotate(o,d^1);
}
o->maintain();
}
void remove(node* &o,int x){
int d=o->cmp(x);
if (d==-1){
node* u=o;
if (o->ch[0]!=NULL && o->ch[1]!=NULL){
int d2=(o->ch[0]->r)> (o->ch[1]->r)?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 rank(node* t,int key){
if (t==NULL)return 1;
if (key<=t->v)return rank(t->ch[0],key);
else return rank(t->ch[1],key)+1+ ( t->ch[0]==NULL?0:t->ch[0]->s );
}
int kth(node* o,int k){
int t= ( o->ch[0]==NULL? 0:o->ch[0]->s ) +1;
if (k==t)return o->v;
else if (k<t)return kth(o->ch[0],k);
else return kth(o->ch[1],k-t );
}
要注意:
1、这个代码较短,但常数较大
2、这个代码不能实现高效的prev和next操作
3、如果在Linux上交这个代码会CE,因为Linux不支持相同名字不同类型的两个函数
4、这个版本有个限制,不能出现重复数据,修改的话insert把cmp换掉即可
用这个Treap试了下营业额统计,和Splay不相上下,Treap的next和prev操作比Splay慢,否则的话貌似要比Splay快很多
BST的缺陷是因为人为搞单调数据才爆的,随机数据还是logn的时间,Treap的思想也基于此,给每个添加的点赋一个随机优先值,然后整个树关于优先值是一个堆。
CLRS上要求读者证明插入节点后期望的旋转次数小于2,看到之后就吓呆了
接下来是具体实现,lrj的代码实在是太漂亮了,忍不住贴出来
const int maxn=1000;
double random(){ return (double)rand()/RAND_MAX;}
int random(int m) { return (int)((m-1)*random()+0.5);}
struct node{
node* ch[2];
int v,r,s;
node (int v=0):v(v) {ch[0]=ch[1]=NULL;s=1;r=random(maxn);}
int cmp(int x)const{
if (v==x)return -1;
return x<v?0:1;
}
void maintain(){
s=1;
if (ch[0]!=NULL)s+=ch[0]->s;
if (ch[1]!=NULL)s+=ch[1]->s;
}
};
node* root;
void rotate(node* & o,int d){
node* k=o->ch[d^1]; o->ch[d^1]=k->ch[d]; k->ch[d]=o;
o->maintain(); k->maintain(); o=k;
}
node* find1(node* o,int x){
if (o==NULL)return NULL;
if (o->v==x)return o;
return o->v<x? find1(o->ch[1],x):find1(o->ch[0],x);
}
void insert(node* &o,int x){
if (o==NULL){o=new node(x);}
else {
int d=o->cmp(x);
insert(o->ch[d],x);if (o->ch[d]->r>o->r)rotate(o,d^1);
}
o->maintain();
}
void remove(node* &o,int x){
int d=o->cmp(x);
if (d==-1){
node* u=o;
if (o->ch[0]!=NULL && o->ch[1]!=NULL){
int d2=(o->ch[0]->r)> (o->ch[1]->r)?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 rank(node* t,int key){
if (t==NULL)return 1;
if (key<=t->v)return rank(t->ch[0],key);
else return rank(t->ch[1],key)+1+ ( t->ch[0]==NULL?0:t->ch[0]->s );
}
int kth(node* o,int k){
int t= ( o->ch[0]==NULL? 0:o->ch[0]->s ) +1;
if (k==t)return o->v;
else if (k<t)return kth(o->ch[0],k);
else return kth(o->ch[1],k-t );
}
要注意:
1、这个代码较短,但常数较大
2、这个代码不能实现高效的prev和next操作
3、如果在Linux上交这个代码会CE,因为Linux不支持相同名字不同类型的两个函数
4、这个版本有个限制,不能出现重复数据,修改的话insert把cmp换掉即可
用这个Treap试了下营业额统计,和Splay不相上下,Treap的next和prev操作比Splay慢,否则的话貌似要比Splay快很多
相关文章推荐
- Treap 学习笔记
- 平衡树:treap学习笔记(1)
- Treap学习笔记
- 非旋转 Treap 学习笔记(一)
- 平衡树(treap)学习笔记
- 可持久化treap学习笔记
- Treap-平衡树学习笔记
- [中级数据结构学习笔记]一、Treap
- Treap 学习笔记
- 非旋Treap 学习笔记
- 无旋Treap学习笔记+例题
- 非旋转 Treap 学习笔记(二)
- [总结] fhq_Treap 学习笔记
- 平衡树:treap学习笔记(2)
- [普通平衡树treap]【学习笔记】
- C++ Primer学习笔记之第12章-泛型-踩在巨人的脚背上-prog12.cpp程序
- 《精通CSS:高级Web标准解决方案》学习笔记之一:基础知识
- 编译器、连接器学习笔记--(一)--综述
- JPA学习笔记---JPA实体Bean的建立---链接上一个博文:对实体Bean中属性进行操作:保存日期类型,设置字段的长度,名字,是否为空,可以声明枚举字段;可以存放二进制数据,可以存放
- JavaScript学习笔记(二十二) 声明依赖