【数据结构】Treap

数据结构-Treap


前置知识

思路

Treap 是平衡树的一种。
Treap=tree+heap=树堆 确实是这样的。
Treap 的每个节点维护两个值,原本的点权和随机生成的权重。Treap 对于点权满足 BST 的性质,对权重满足堆的性质,就可以达到 O ( log ⁡ n ) O(\log n) O(logn) 的期望复杂度了。
下面来讲一下 Treap 各主要函数的实现。


Insert \text{Insert} Insert

插入节点。
先根据 BST 的性质插入元素为叶子节点,然后从叶子节点回溯至根节点,对不符合堆的性质的失衡子树做相应的旋转。


Delete \text{Delete} Delete

删除节点。
需要将该节点逐层旋转至叶子节点,同时需要维护堆的性质,最终在叶子处删除节点。


GetRnk \text{GetRnk} GetRnk

查询值对应的排名,该函数返回的其实是比该元素小的元素的数量。
利用 BST 的性质,每次向下需要加上该节点的同权值元素数,若向右子树递归则需要再加上左子树的大小。


GetVal \text{GetVal} GetVal

查询排名对应的值。
利用 BST 的性质,每次向下时重新计算目标节点在子树中的排名,递归即可。


GetPre \text{GetPre} GetPre

查询节点的前驱,即最大的比该元素小的数。
从根节点向下,若节点比该元素权值小,就向右子树递推,否则向左子树递推即可。


GetNxt \text{GetNxt} GetNxt

查询节点的后继,即最小的比该节点大的数。
从根节点向下,若节点比该元素权值大,就向左子树递推,否则向右子树递推即可。


数据结构参数
  • 单次修改时间复杂度: Θ ( log ⁡ n ) \Theta(\log n) Θ(logn)
  • 单次查询时间复杂度: Θ ( log ⁡ n ) \Theta(\log n) Θ(logn)
  • 空间复杂度: Θ ( n ) \Theta(n) Θ(n)

实现代码

代码比较长。考验码力。

int root,tot;//root 根 //tot 节点数 
struct node{
	int v,k,l,r,s,c;//v 权值 //k 随机权重 //l 左子节点 //r 右子节点 //s 子树大小 //c 同权值节点个数 
}t[N];//Treap
void Pushup(int p){//更新子树大小 
	t[p].s=t[t[p].l].s+t[t[p].r].s+t[p].c;
}
void Zag(int &p){//左旋 
	int q=t[p].r;
	t[p].r=t[q].l;
	t[q].l=p;p=q;
	Pushup(t[p].l);Pushup(p);
}
void Zig(int &p){//右旋 
	int q=t[p].l;
	t[p].l=t[q].r;
	t[q].r=p;p=q;
	Pushup(t[p].r);Pushup(p);
}
int  Create(int x){//创建节点 
	int k=++tot;
	t[k].v=x;
	t[k].s=t[k].c=1;
	t[k].l=t[k].r=0;
	t[k].k=rand();
	return k;
}
void Insert(int &p,int x){//插入 
	if (!p){p=Create(x);return;}
	if (x==t[p].v){t[p].c++;t[p].s++;return;}
	if (x<t[p].v) Insert(t[p].l,x);
	if (x>t[p].v) Insert(t[p].r,x);
	if (t[p].k<t[t[p].l].k) Zig(p);
	if (t[p].k<t[t[p].r].k) Zag(p);
	Pushup(p);
}
void Delete(int &p,int x){//删除 
	if (!p) return;
	if (t[p].v==x){
		if (t[p].c>1){t[p].c--;Pushup(p);return;}
		if (t[p].l||t[p].r){
			if (!t[p].r||t[t[p].l].k>t[t[p].r].k) Zig(p),Delete(t[p].r,x);
			else Zag(p),Delete(t[p].l,x);
			Pushup(p);
		}
		else p=0;
		return;
	}
	if (x<t[p].v) Delete(t[p].l,x);
	else Delete(t[p].r,x);
	Pushup(p);
}
int  GetRnk(int p,int v){//获取排名,该函数返回的其实是比该元素小的元素的数量
	if (!p) return 1;
	if (v==t[p].v) return t[t[p].l].s+1;
	else if (v<t[p].v) return GetRnk(t[p].l,v);
	else return t[t[p].l].s+t[p].c+GetRnk(t[p].r,v);
}
int  GetVal(int p,int x){//获取权值 
	if (!p) return INF;
	if (t[t[p].l].s>=x) return GetVal(t[p].l,x);
	if (t[t[p].l].s+t[p].c>=x) return t[p].v;
	return GetVal(t[p].r,x-t[t[p].l].s-t[p].c);
}
int  GetPre(int x){//获取前驱 
	int p=root,pre;
	while (p)
		if (t[p].v<x) pre=t[p].v, p=t[p].r;
		else p=t[p].l;
	return pre;
}
int  GetNxt(int x){//获取后继 
	int p=root,nxt;
	while (p)
		if (t[p].v>x) nxt=t[p].v, p=t[p].l;
		else p=t[p].r;
	return nxt;
}
void BuildT(){//建树
	Insert(root,INF);Insert(root,-INF);
	Pushup(root);
	if (t[root].k<t[t[root].l].k) Zig(root);
}

练习
  • 35
    点赞
  • 46
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值