【数据结构】范浩强Treap(非旋转平衡树)&可持久化Treap总结

范浩强Treap

这是一种很神奇的数据结构(似乎我每个数据结构都要这么说)
尽管可能是我的模板太丑了,速度实测出来似乎并不比splay快,不过要做到可持久化的话,
这种数据结构就是首选。
另外,这种数据结构相当好写。

简单地说,这种treap基于两种操作:

Merge(int x,int y)->将x的子树和y的子树合并起来,且满足 x的子树的最大值小于等于y子树的最小值,复杂度O(logN)
Split ( int x,int k ) ->将x的 最小的k个值/小于等于k的值 与另外的部分分离出来,复杂度O(logN)

首先是Merge操作:
因为已知x的子树最大值小于等于y子树的最小值,
很容易想到必定是y链接在x的右儿子节点,或者x链接在y的左儿子节点(详见代码)
为了使深度尽量小,我们使用随机数,来判断x为y的儿子,或者y为x的儿子

int Merge(int x,int y){
    if(x==0) return y;
    if(y==0) return x;
    if(Tree[x].fix<Tree[y].fix){
        Tree[x].ch[1]=Merge(Tree[x].ch[1],y);//将y连在x的右儿子节点
        update(x);
        return x;
    }
    else{
        Tree[y].ch[0]=Merge(x,Tree[y].ch[0]);//将x连在y左儿子节点
        update(y);
        return y;
    }
}

接下来是Split操作
(有两种不同的Split方式,根据题目,可以自行选用合适的一种)
首先我们要将这颗树分成两部分,
以下代码中first为前半部分,second为后半部分

pair<int,int> Split(int x,int k){
    if(x==0) return make_pair(0,0);
    pair<int,int> y;
    if(Tree[x].key<=k){//将子树中值小于等于k的分离出来
        y=Split(Tree[x].ch[1],k);//x此时必定在小于等于k的部分,但x的右儿子是否在这部分并不确定,所以递归操作到叶子节点为止
        Tree[x].ch[1]=y.first;//将x的子树中与x在同一部分的相连接,
        update(x);
        y.first=x;
    }
    else{//与上面完全相反
        y=Split(Tree[x].ch[0],k);
        Tree[x].ch[0]=y.second;
        update(x);
        y.second=x;
    }
    return y;
}

第二种:

pair<int,int> Split(int x,int k){
    if(x==0) return make_pair(0,0);
    pair<int,int> y;
    if(Tree[Tree[x].ch[0]].sum<k){//将子树最小的k个分离出来(如有相同可能会多于k个)
        y=Split(Tree[x].ch[1],k);
        Tree[x].ch[1]=y.first;
        update(x);
        y.first=x;
    }
    else{
        y=Split(Tree[x].ch[0],k);
        Tree[x].ch[0]=y.second;
        update(x);
        y.second=x;
    }
    return y;
}

现在,基于这两种操作,我们就可以写出来一个平衡树了(是不是很好写!!)
那么首先是插入操作:

void Insert(int val){
    pair<int,int> x=Split(root,val);
    Tree[++ncnt]=node(val);//初始化
    Tree[ncnt].ch[0]=0;
    Tree[ncnt].ch[1]=0;
    root=Merge(Merge(x.first,ncnt),x.second);
}

是不是很简单?

然后是删除操作:

void Del(int val){
    pair<int,int> x=Split(root,val);
    pair<int,int> y=Split(x.first,val-1);
    y.second=Merge(Tree[y.second].ch[0],Tree[y.second].ch[1]);
    root=Merge(Merge(y.first,y.second),x.second);
}

是不是很简单??


再来一个:求平衡树中第k小的数

int find_id(int x,int k){
    if(x==0)
        return 0;
    int ch0=Tree[x].ch[0];
    if(Tree[ch0].sum>=k)
        return find_id(ch0,k);
    if(Tree[ch0].sum+1>=k)
        return Tree[x].key;
    return find_id(Tree[x].ch[1],k-Tree[ch0].sum-1);
}

额。。其实似乎这和普通的平衡树并没有不同。。。

求一个数val在平衡树中的排名(这个值可以不在树中)

int get_kth(int k){
	//如果Split是按照前k个分离,还需要加一句int k1=find_id(root,k);并把下面的k-1改为k1
    pair<int,int> x=Split(root,k-1);//这是Split按照小于等于k的分离的写法
    int ans=Tree[x.first].sum+1;
    root=Merge(x.first,x.second);
    return ans;
}

是不是很简单???
总而言之,这是一个相当好写的平衡树,还不熟悉的话,可以多做几次cqoi的普通平衡树

可持久化Treap

其实,如果你学过其他的可持久化数据结构,
那么这部分就非常简单了(打个广告)

其实和主席树一样,我们只需要用n个root节点,并且每次插入时,将路径上每个节点复制一个即可。
这是可持久化的Split

pair<int,int> Split(int x,int k){
    if(x==0)
        return make_pair(0,0);
    int newone;
    pair<int,int> y;
    if(Tree[x].key<=k){
        newone=++ncnt;
        Tree[newone]=Tree[x];
        y=Split(Tree[newone].ch[1],k);
        Tree[newone].ch[1]=y.first;
        update(newone);
        y.first=newone;
    }
    else{
        newone=++ncnt;
        Tree[newone]=Tree[x];
        y=Split(Tree[newone].ch[0],k);
        Tree[newone].ch[0]=y.second;
        update(newone);
        y.second=newone;
    }
    return y;
}

但其实有时候Merge操作并不需要复制新节点:
原因很简单,如果Merge的两个子树都在同一个时间版本内,
那复制新节点就毫无必要了。

int Merge(int x,int y){
    if(x==0) return y;
    if(y==0) return x;
    if(Tree[x].fix<Tree[y].fix){
        Tree[x].ch[1]=Merge(Tree[x].ch[1],y);
        update(x);
        return x;
    }
    else{
        Tree[y].ch[0]=Merge(x,Tree[y].ch[0]);
        update(y);
        return y;
    }
}

例题:洛谷3835普通可持久化平衡树

#include
#include
#include
#include
#define SF scanf
#define PF printf
#define MAXN 500010
using namespace std;
struct node{
    int ch[2];
    int key,sum,fix;
    node () {}
    node (int key1):key(key1),fix(rand()),sum(1) {}
}Tree[MAXN*30];
int ncnt,root[MAXN];
void update(int x){
    Tree[x].sum=Tree[Tree[x].ch[0]].sum+Tree[Tree[x].ch[1]].sum+1;
}
int Merge(int x,int y){
    if(x==0) return y;
    if(y==0) return x;
    if(Tree[x].fix<Tree[y].fix){
        Tree[x].ch[1]=Merge(Tree[x].ch[1],y);
        update(x);
        return x;
    }
    else{
        Tree[y].ch[0]=Merge(x,Tree[y].ch[0]);
        update(y);
        return y;
    }
}
pair<int,int> Split(int x,int k){
    if(x==0)
        return make_pair(0,0);
    int newone;
    pair<int,int> y;
    if(Tree[x].key<=k){
        newone=++ncnt;
        Tree[newone]=Tree[x];
        y=Split(Tree[newone].ch[1],k);
        Tree[newone].ch[1]=y.first;
        update(newone);
        y.first=newone;
    }
    else{
        newone=++ncnt;
        Tree[newone]=Tree[x];
        y=Split(Tree[newone].ch[0],k);
        Tree[newone].ch[0]=y.second;
        update(newone);
        y.second=newone;
    }
    return y;
}
int find_kth(int now,int val){
    pair<int,int> x=Split(root[now],val-1);
    int ans=Tree[x.first].sum+1;
    root[now]=Merge(x.first,x.second);
    return ans;
}
int get_kth(int x,int k){
    if(x==0)
        return 0;
    int ch0=Tree[x].ch[0];
    if(Tree[ch0].sum>=k)
        return get_kth(ch0,k);
    if(Tree[ch0].sum+1==k)
        return Tree[x].key;
    return get_kth(Tree[x].ch[1],k-Tree[ch0].sum-1);
}
int get_pre(int now,int val){
    int k=find_kth(now,val);
    return get_kth(root[now],k-1);
}
int get_bac(int now,int val){
    pair<int,int> x=Split(root[now],val);
    int ans=get_kth(x.second,1);
    root[now]=Merge(x.first,x.second);
    return ans;
}
void Insert(int val,int now){
    pair<int,int> x=Split(root[now],val);
    Tree[++ncnt]=node(val);
    Tree[ncnt].ch[0]=0;
    Tree[ncnt].ch[1]=0;
    root[now]=Merge(Merge(x.first,ncnt),x.second);
}
void Delete(int val,int now){
    pair<int,int> x=Split(root[now],val);
    pair<int,int> y=Split(x.first,val-1);
    if(Tree[y.second].key==val)
        y.second=Merge(Tree[y.second].ch[0],Tree[y.second].ch[1]);
    root[now]=Merge(Merge(y.first,y.second),x.second);
}
int n,las,x,tag;
int main(){
    SF("%d",&n);
    Insert(-2147483647,0);
    Insert(2147483647,0);
    for(int i=1;i<=n;i++){
        SF("%d%d%d",&las,&tag,&x);
        if(Tree[root[las]].sum!=0){
            root[i]=++ncnt;
            Tree[root[i]]=Tree[root[las]];
        }
        if(tag==1)
            Insert(x,i);
        if(tag==2)
            Delete(x,i);
        if(tag==3)
            PF("%d\n",find_kth(i,x)-1);
        if(tag==4)
            PF("%d\n",get_kth(root[i],x+1));
        if(tag==5)
            PF("%d\n",get_pre(i,x));
        if(tag==6)
            PF("%d\n",get_bac(i,x));
    }
}

指针版

#include
#include
#include
#include
#define SF scanf
#define PF printf
#define MAXN 500010
#define INF 2147483647
using namespace std;
struct node{
	node *ch[2];
	int val,sum,fix;
	void pushup(){
		sum=ch[0]->sum+ch[1]->sum+1;	
	}
}Tree[MAXN*24];
node *NIL=Tree,*ncnt=Tree;
typedef pair<node*,node*> PNN;
void Newnode(node *x,int val){
	x->val=val;
	x->sum=1;
	x->fix=rand();
	x->ch[0]=x->ch[1]=NIL;	
}
void init(){
	NIL->ch[0]=NIL->ch[1]=NIL;	
}
node *Merge(node *x,node *y){
	if(x==NIL)
		return y;
	if(y==NIL)
		return x;
	if(x->fix>y->fix){
		x->ch[1]=Merge(x->ch[1],y);
		x->pushup();
		return x;
	}
	else{
		y->ch[0]=Merge(x,y->ch[0]);
		y->pushup();
		return y;
	}
}
PNN Split(node *x,int val){
	if(x==NIL)
		return make_pair(NIL,NIL);
	node *newp=++ncnt;
	PNN y;
	if(x->val<=val){
		*newp=*x;
		y=Split(x->ch[1],val);
		newp->ch[1]=y.first;
		newp->pushup();
		y.first=newp;
	}
	else{
		*newp=*x;
		y=Split(x->ch[0],val);
		newp->ch[0]=y.second;
		newp->pushup();
		y.second=newp;
	}
	return y;
}
void Ins(node *&rt,int val){
    PNN y=Split(rt,val);
    Newnode(++ncnt,val);
    node *x=ncnt;
    node *z=Merge(x,y.second);
    rt=Merge(y.first,z);
}   
void Del(node *&rt,int val){
    PNN x=Split(rt,val);
    PNN y=Split(x.first,val-1);
    if(y.second->val==val)
		y.second=Merge(y.second->ch[0],y.second->ch[1]);
    rt=Merge(Merge(y.first,y.second),x.second); 
}
node *find_kth(node *x,int k){
    if(x==NIL)
        return x;
    if(x->ch[0]->sum>=k)
        return find_kth(x->ch[0],k);
    if(x->ch[0]->sum+1>=k)
        return x;
    return find_kth(x->ch[1],k-(x->ch[0]->sum)-1);
}
node *find_nxt(node *x,int d){
    while(x->ch[d]!=NIL)
        x=x->ch[d];
    return x;
}
node *root[MAXN];
int n;
int main(){
	SF("%d",&n);
	init();
	root[0]=NIL;
	Ins(root[0],-INF);
	Ins(root[0],INF);
	int las,tag,x;
	for(int i=1;i<=n;i++){
		SF("%d%d%d",&las,&tag,&x);
		root[i]=++ncnt;
		*root[i]=*root[las];
		if(tag==1)
			Ins(root[i],x);
		if(tag==2)
			Del(root[i],x);
		if(tag==3){
            PNN y=Split(root[i],x-1);
            PF("%d\n",y.first->sum);
            root[i]=Merge(y.first,y.second);
        }
        if(tag==4){
        	x++;
            PF("%d\n",find_kth(root[i],x)->val);
        }
        if(tag==5){
            PNN y=Split(root[i],x-1);
            PF("%d\n",find_nxt(y.first,1)->val);
            root[i]=Merge(y.first,y.second);
        }
        if(tag==6){
            PNN y=Split(root[i],x);
            PF("%d\n",find_nxt(y.second,0)->val);
            root[i]=Merge(y.first,y.second);
        }
	}
}

你可能感兴趣的:(总结,数据结构,平衡树,可持久化)