【模板】Treap(详细注释)+BZOJ3224

有关Treap,很多博客都给出了详细的讲解,这里就只给出Treap的模板代码和详细的注释,希望大家对Treap有更详细的理解。

模板如下(同时也是BZOJ3224的代码)。

注意在BZOJ和POJ等OJ上提交的时候不要使用srand()函数,否则会RE。

#include<cstdio>
#include<ctime>
#include<algorithm>
using namespace std;
const int maxn=100005;
int n;
struct Node{
	int LeftChild,RightChild,v,size,w,rnd;
	//w 表示 相同元素个数,v 表示 权值,size 表示 子树大小,rnd 表示随机权值
};
struct Treap{
	int root,ans,size;
	Node Tree[maxn];
	void Update(int k){//更新结点信息
		Tree[k].size=Tree[Tree[k].LeftChild].size+Tree[Tree[k].RightChild].size+Tree[k].w;
	}
	void RTurn(int &k){//旋转操作,不明白画个图理解一下吧.
		int t=Tree[k].LeftChild;Tree[k].LeftChild=Tree[t].RightChild;Tree[t].RightChild=k;
		Tree[t].size=Tree[k].size;Update(k),k=t;
	}
	void LTurn(int &k){//旋转操作,不明白画个图理解一下吧.
		int t=Tree[k].RightChild;Tree[k].RightChild=Tree[t].LeftChild;Tree[t].LeftChild=k;
		Tree[t].size=Tree[k].size;Update(k),k=t;
	}
	void insert(int &k,int x){//插入操作.
		if(k==0){//k==0需新建节点.
			size++;k=size;//新建节点.
			Tree[k].size=Tree[k].w=1;Tree[k].v=x;Tree[k].rnd=rand();//新节点初始化.
			return;
		}
		Tree[k].size++;//插入k所在的子树中,size++.
		if (Tree[k].v==x) Tree[k].w++;
		//找到相同键值的节点,键值出现次数+1.
		else if (x>Tree[k].v){//仍未找到插入位置,根据性质向左右子查找.
			insert(Tree[k].RightChild,x);//插入右子.
			if (Tree[Tree[k].RightChild].rnd<Tree[k].rnd) LTurn(k);
			//根据随机优先级旋转树来保持堆性质.
			//此处维护小根堆,大根堆亦可,将小于号改为大于号即可.
		}
		else{
			insert(Tree[k].LeftChild,x);//插入左子.
			if (Tree[Tree[k].LeftChild].rnd<Tree[k].rnd) RTurn(k);
			//维护堆性质.
		}
	}
	void erase(int &k,int x){//删除操作.
		if (k==0) return ;//未找到待删除节点,返回.
		if (Tree[k].v==x){//找到待删除节点.
			if (Tree[k].w>1){//若键值出现过多于一次.
				Tree[k].w--,Tree[k].size--;//次数-1,子树大小-1.
				return ;
			}
			if (Tree[k].LeftChild*Tree[k].RightChild==0) k=Tree[k].LeftChild+Tree[k].RightChild;
			//如果没有儿子或只有一个儿子,那么直接删除.
			else if (Tree[Tree[k].LeftChild].rnd<Tree[Tree[k].RightChild].rnd) RTurn(k),erase(k,x);
			else LTurn(k),erase(k,x);
			//如果有两个儿子,就在两个儿子中选择随机优先级小的那个节点替换当前节点(通过旋转操作).
			//再递归删除.
		}
		else if (x>Tree[k].v) Tree[k].size--,erase(Tree[k].RightChild,x);
		else Tree[k].size--,erase(Tree[k].LeftChild,x);
		//还没有找到待删除节点,继续根据性质递归查找.
		//因为已经确定待删除节点再子树k中,就可以顺便将k的子树大小-1.
	}
	int kth(int k,int x){//查询第k大.
		//此处k为节点编号,x为要查询的值.
		if (k==0) return 0;//递归到空节点,直接返回0.
		if (x<=Tree[Tree[k].LeftChild].size) return kth(Tree[k].LeftChild,x);
		//k小于左子的子树大小,说明第k大在左子树中,查询左子树中的第k大.
		else if (x>Tree[Tree[k].LeftChild].size+Tree[k].w) return kth(Tree[k].RightChild,x-Tree[Tree[k].LeftChild].size-Tree[k].w);
		//k大于左子的子树大小加本节点键值出现次数,说明第k大在右子中.
		//查询右子树中的第k-Tree[Tree[k].LeftChild].size+Tree[k].w(减去左子树的大小和本节点键值出现次数)大.
		else return Tree[k].v;
		//此时的x>Tree[Tree[k].LeftChild].size且x<=Tree[Tree[k].LeftChild].size+Tree[k].w
		//说明当前的节点就是第k大,返回即可.
	}
	int rank(int k,int x){//查询x的排名(x不一定在Treap中).
		if (k==0) return 0;//递归到空节点,直接返回0.
		if (Tree[k].v==x) return Tree[Tree[k].LeftChild].size+1;
		//x存在于Treap中,且已找到,返回左子树的大小+1即为排名.
		else if (x>Tree[k].v) return Tree[Tree[k].LeftChild].size+Tree[k].w+rank(Tree[k].RightChild,x);
		//根据性质递归查找.
		//在右子中查找,应返回其在右子中的排名+左子的子树大小和本节点键值出现次数.
		else return rank(Tree[k].LeftChild,x);
		//在左子中查找,返回其在左子中的排名即可.
	}
	void predecessor(int k,int x){//查询前驱.
		if(k==0) return;//递归到空节点,返回.
		if(Tree[k].v<x) this->ans=k,predecessor(Tree[k].RightChild,x);
		//本节点的键值小于查询值,更新答案,向更大的方向查找(右子).
		else predecessor(Tree[k].LeftChild,x);
		//本节点的键值大于等于查询值,向更小的方向查找(左子).
	}
	void successor(int k,int x){//查询后继.
		if (k==0) return;//递归到空节点,返回. 
		if (Tree[k].v>x) this->ans=k,successor(Tree[k].LeftChild,x);
		//本节点键值大于查询值,更新答案,向更小的方向查找(左子).
		else successor(Tree[k].RightChild,x);
		//本节点的键值大于等于查询值,向更大的方向查找(右子).
	}
	void Insert(int x){insert(root,x);}
	void Erase(int x){erase(root,x);}
	int Kth(int k){return kth(root,k);}
	int Rank(int x){return rank(root,x);}
	int Predecessor(int x){predecessor(root,x);return Tree[ans].v;}
	int Successor(int x){successor(root,x);return Tree[ans].v;}
}Treap;
int main(){
//	freopen("BZOJ3224.in","r",stdin);
//	freopen("BZOJ3224.out","w",stdout);
//	srand(time(NULL));
	scanf("%d",&n);
	for (int i=1,opt,x;i<=n;i++){
		scanf("%d%d",&opt,&x);
		if (opt==1) Treap.Insert(x);
		if (opt==2) Treap.Erase(x);
		if (opt==3) printf("%d\n",Treap.Rank(x));
		if (opt==4) printf("%d\n",Treap.Kth(x));
		if (opt==5) printf("%d\n",Treap.Predecessor(x));
		if (opt==6) printf("%d\n",Treap.Successor(x));
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/zhanxufeng/article/details/82702100