您的位置:首页 > 其它

Treap

2015-10-30 11:40 316 查看
Treap
Treap=Tree+Heap,Treap是一个二叉搜索树,它的左右子树都分别是一个Treap,和一般二叉排序树不同的是,Treap记录一个额外的数据(v),就是优先级(rank)。Treap在以关键字构成二叉排序树的同时,还满足堆的性质,不过Treap不一定是完全二叉树,而堆必须是完全二叉树.

1.结构体定义:

struct data{

int l,r; //左右子树

int v; //当前节点的值

int size; //由该节点为根构成的子树的大小

int rnd; //当前节点的优先级

int w; //和当前节点值相同的个数

}tr[MAXN];

2.Treap的性质:

(1) 如果b是a的左子树,那么tree[a].v>tree[b].v;

(2) 如果c是a的右子树,那么tree[a].v<tree[c].v;

(3) 如果b,c分别是a的左右子树,那么tree[a].rank<tree[b].rank&& tree[a].rank<tree[c].rank;

3.Treap的旋转:跟普通的旋转一样

代码实现:

void update(int k)//更新结点信息

{

tr[k].size=tr[tr[k].l].size+tr[tr[k].r].size+tr[k].w;

}

void rturn(int &k)

{

int t=tr[k].l;tr[k].l=tr[t].r;tr[t].r=k;

tr[t].size=tr[k].size;update(k);k=t;

}

void lturn(int &k)

{

int t=tr[k].r;tr[k].r=tr[t].l;tr[t].l=k;

tr[t].size=tr[k].size;update(k);k=t;

}

4.Treap的插入:

(1)按照二叉树的插入方法,将结点插入到树中

(2)根据堆的性质(我们这里为最小堆)和优先级的大小调整结点位置。

代码实现:

void insert(int &k,int x){

if(k==0){

size++;k=size;

tr[k].size=tr[k].w=1;tr[k].v=x;tr[k].rnd=rand();

return;

}

tr[k].size++;

if(tr[k].v==x)tr[k].w++;

else if(x>tr[k].v){

insert(tr[k].r,x);

if(tr[tr[k].r].rnd<tr[k].rnd)lturn(k); //维护堆性质

}

else {

insert(tr[k].l,x);

if(tr[tr[k].l].rnd<tr[k].rnd)rturn(k);

}

}

5.Treap的删除:

(1)利用二叉树的性质,找到和x值相同的节点

(2)如果和x值相同的节点的个数有多个,那么只需要将size和w减一即可

(3)如果和x值相同的节点的个数只有一个,而且其存在一个子结点,那么当前需要删除的结点的值应该更新为存在的子节点的值,如果存在两个子结点,那么判断两个子结点的优先级,优先级值小的被更新为删除结点的值。

代码实现:

void del(int &k,int x){

if(k==0)return;

if(tr[k].v==x){

if(tr[k].w>1){

tr[k].w--;tr[k].size--;return;//若不止相同值的个数有多个,删去一个

}

if(tr[k].l*tr[k].r==0)k=tr[k].l+tr[k].r;//有一个儿子为空

else if(tr[tr[k].l].rnd<tr[tr[k].r].rnd)

rturn(k),del(k,x);

else lturn(k),del(k,x);

}

else if(x>tr[k].v)

tr[k].size--,del(tr[k].r,x);

else tr[k].size--,del(tr[k].l,x);

}

6.Treap的应用:

(1) 查询x数的排名(若有多个相同的数,因输出最小的排名)

代码实现:

int query_rank(int k,int x){

if(k==0)return 0;

if(tr[k].v==x)return tr[tr[k].l].size+1;

else if(x>tr[k].v)

return tr[tr[k].l].size+tr[k].w+query_rank(tr[k].r,x);

else return query_rank(tr[k].l,x);

}

(2) 查询排名为x的数

代码实现:

int query_num(int k,int x){

if(k==0)return 0;

if(x<=tr[tr[k].l].size)

return query_num(tr[k].l,x);

else if(x>tr[tr[k].l].size+tr[k].w)

return query_num(tr[k].r,x-tr[tr[k].l].size-tr[k].w);

else return tr[k].v;

}

(4) 求x的前驱(前驱定义为小于x,且最大的数)和求x的后继(后继定义为大于x,且最小的数)

代码实现:

int ans=0;

void query_pro(int k,int x){ //前驱

if(k==0)return;

if(tr[k].v<x){

ans=k;query_pro(tr[k].r,x);

}

else query_pro(tr[k].l,x);

}

cout<<tree[ans].v<<endl;

void query_sub(int k,int x){ //后继

if(k==0)return;

if(tr[k].v>x){

ans=k;query_sub(tr[k].l,x);

}

else query_sub(tr[k].r,x);

}

cout<<tree[ans].v<<endl;

7.完整代码:

struct data{

int l,r,v,size,rnd,w;

}tr[MAXN];

int n,size,root,ans;

void update(int k)//更新结点信息

{

tr[k].size=tr[tr[k].l].size+tr[tr[k].r].size+tr[k].w;

}

void rturn(int &k)

{

int t=tr[k].l;tr[k].l=tr[t].r;tr[t].r=k;

tr[t].size=tr[k].size;update(k);k=t;

}

void lturn(int &k)

{

int t=tr[k].r;tr[k].r=tr[t].l;tr[t].l=k;

tr[t].size=tr[k].size;update(k);k=t;

}

void insert(int &k,int x)

{

if(k==0)

{

size++;k=size;

tr[k].size=tr[k].w=1;tr[k].v=x;tr[k].rnd=rand();

return;

}

tr[k].size++;

if(tr[k].v==x)tr[k].w++;//每个结点顺便记录下与该节点相同值的数的个数

else if(x>tr[k].v)

{

insert(tr[k].r,x);

if(tr[tr[k].r].rnd<tr[k].rnd)lturn(k);//维护堆性质

}

else

{

insert(tr[k].l,x);

if(tr[tr[k].l].rnd<tr[k].rnd)rturn(k);

}

}

void del(int &k,int x)

{

if(k==0)return;

if(tr[k].v==x)

{

if(tr[k].w>1)

{

tr[k].w--;tr[k].size--;return;//若不止相同值的个数有多个,删去一个

}

if(tr[k].l*tr[k].r==0)k=tr[k].l+tr[k].r;//有一个儿子为空

else if(tr[tr[k].l].rnd<tr[tr[k].r].rnd)

rturn(k),del(k,x);

else lturn(k),del(k,x);

}

else if(x>tr[k].v)

tr[k].size--,del(tr[k].r,x);

else tr[k].size--,del(tr[k].l,x);

}

int query_rank(int k,int x)

{

if(k==0)return 0;

if(tr[k].v==x)return tr[tr[k].l].size+1;

else if(x>tr[k].v)

return tr[tr[k].l].size+tr[k].w+query_rank(tr[k].r,x);

else return query_rank(tr[k].l,x);

}

int query_num(int k,int x)

{

if(k==0)return 0;

if(x<=tr[tr[k].l].size)

return query_num(tr[k].l,x);

else if(x>tr[tr[k].l].size+tr[k].w)

return query_num(tr[k].r,x-tr[tr[k].l].size-tr[k].w);

else return tr[k].v;

}

void query_pro(int k,int x)

{

if(k==0)return;

if(tr[k].v<x)

{

ans=k;query_pro(tr[k].r,x);

}

else query_pro(tr[k].l,x);

}

void query_sub(int k,int x)

{

if(k==0)return;

if(tr[k].v>x)

{

ans=k;query_sub(tr[k].l,x);

}

else query_sub(tr[k].r,x);

}

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: