数据结构入门1—Treap

debug3个月把洛谷刷屏,终于过了。。。。(一开始写了两百行,后来看了大孙代码才搞点小技巧改短了些)

treap,树堆,是指有一个随机附加域满足堆的性质的二叉搜索树,其结构相当于以随机数据插入的二叉搜索树。其基本操作的期望时间复杂度为O(logn)。相对于其他的平衡二叉搜索树,Treap的特点是实现简单,且能基本实现随机平衡的结构。我们可以看到,如果一个二叉排序树节点插入的顺序是随机的,这样我们得到的二叉排序树大多数情况下是平衡的,即使存在一些极端情况,但是这种情况发生的概率很小,所以我们可以这样建立一颗二叉排序树,而不必要像AVL那样旋转,可以证明随机顺序建立的二叉排序树在期望高度是O(logn),但是某些时候我们并不能得知所有的带插入节点,打乱以后再插入。所以我们需要一种规则来实现这种想法,并且不必要所有节点。也就是说节点是顺序输入的,我们实现这一点可以用Treap。Treap=Tree+Heap。

Treap是一棵二叉排序树,它的左子树和右子树分别是一个Treap,和一般的二叉排序树不同的是,Treap纪录一个额外的数据,就是优先级。Treap在以关键码构成二叉排序树的同时,还满足堆的性质(在这里我们假设节点的优先级大于该节点的孩子的优先级)。但是这里要注意的是Treap和二叉堆有一点不同,就是二叉堆必须是完全二叉树,而Treap可以并不一定是。

以上来自百度百科。

简而言之,treap中序遍历就是原数组排好序后的排列,其中随机出来的优先级满足堆的性质。

与Splay不同之处是Splay的中序遍历就是原数组的排列顺序,而且与Splay的旋转操作等不同。treap比Splay好写(然而。。。)

新手很容易忽略的细节问题是一个数可能会添加多次,需要存一下每个数有多少个,每次查询和修改不要忘了它。

Treap模板题(洛谷)

//Serene
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<ctime>
using namespace std;
const int maxn=100000+10;
int n,tot=0,ans1,ans2,root;

int aa,ff;char cc;
int read() {
	aa=0;ff=1;cc=getchar();
	while((cc<'0'||cc>'9')&&cc!='-') cc=getchar();
	if(cc=='-') ff=-1,cc=getchar();
	while(cc>='0'&&cc<='9') aa=aa*10+cc-'0',cc=getchar();
	return aa*ff;
}

struct Node{
	int num,rnd,son[2],sum,x;
}node[maxn];

void rotate(int &pos,int p) {
	int s=node[pos].son[p];
	node[s].sum=node[pos].sum;
	node[pos].son[p]=node[s].son[!p];
	node[s].son[!p]=pos;
	node[pos].sum=node[pos].x+node[node[pos].son[0]].sum+node[node[pos].son[1]].sum;
	pos=s;
}

void add(int &pos,int x) {
	if(!pos) {
		pos=++tot;node[pos].num=x;node[pos].rnd=rand();
		node[pos].sum=node[pos].x=1;
		return ;
	}
	node[pos].sum++;
	if(node[pos].num==x) {
		node[pos].x++; return;
	}
	int p=x>node[pos].num;
	add(node[pos].son[p],x);
	if(node[node[pos].son[p]].rnd<node[pos].rnd) rotate(pos,p);
}

void del(int &pos,int x) {
	if(!pos) return;
	if(node[pos].num==x) {
		if(node[pos].x>1) {
			node[pos].x--;node[pos].sum--;return;
		}
		if(node[pos].son[0]*node[pos].son[1]==0) {
			pos=node[pos].son[0]+node[pos].son[1]; return;
		}
		int p= node[node[pos].son[1]].rnd<node[node[pos].son[0]].rnd;
		rotate(pos,p);
		node[pos].sum--;
		del(node[pos].son[!p],x);
	}
	else {
		node[pos].sum--;
		if(node[pos].num>x) del(node[pos].son[0],x);
		else del(node[pos].son[1],x);
	}
}

int qrank(int pos,int x) {
	if(node[pos].num==x) return node[node[pos].son[0]].sum+1;
	if(node[pos].num>x) return qrank(node[pos].son[0],x);
	return node[node[pos].son[0]].sum+node[pos].x+qrank(node[pos].son[1],x);
}

int qnum(int pos,int x) {
	if(x>node[node[pos].son[0]].sum&&x<=node[node[pos].son[0]].sum+node[pos].x) return node[pos].num;
	if(x<=node[node[pos].son[0]].sum) return qnum(node[pos].son[0],x);
	return qnum(node[pos].son[1],x-node[pos].x-node[node[pos].son[0]].sum);
}

void q1(int pos,int x) {
	if(!pos) return;
	if(x>node[pos].num) {
		ans1=node[pos].num;
		q1(node[pos].son[1],x);
	}
	else q1(node[pos].son[0],x);
}

void q2(int pos,int x) {
	if(!pos) return;
	if(x<node[pos].num) {
		ans2=node[pos].num;
		q2(node[pos].son[0],x);
	}
	else q2(node[pos].son[1],x);
}

int main() {
	srand((unsigned)time(NULL));
	n=read();
	int opt,x,y;
	for(int i=1;i<=n;++i) {
		opt=read();x=read();
		if(opt==1) add(root,x);
		else if(opt==2) del(root,x);
		else if(opt==3) printf("%d\n",qrank(root,x));
		else if(opt==4) printf("%d\n",qnum(root,x));
		else if(opt==5) q1(root,x),printf("%d\n",ans1);
		else q2(root,x),printf("%d\n",ans2);
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值