平衡树入门——Treap

平衡树入门——Treap

挖坑一直爽,一直挖坑一直爽。这个坑大概鸽了一年。

1 平衡树

平衡树(Balance Tree,BT) 指的是,任意节点的子树的高度差都小于等于1。常见的符合平衡树的有,B树(多路平衡搜索树)、AVL树(二叉平衡搜索树)等。平衡树可以完成集合的一系列操作, 时间复杂度和空间复杂度相对于“2-3树”要低,在完成集合的一系列操作中始终保持平衡,为大型数据库的组织、索引提供了一条新的途径。

\(Treap\) 是每个 OIer 入门必学平衡树。

2 变量简介

2.1 节点

struct node{
	int size,cnt;
	int val,rad;
	int l,r;
};
node tree[N];
int tail,root;

其中 \(size\) 是这颗子树的大小,\(cnt\) 表示的是这个节点所代表权值的个数,\(val\) 表示的是节点代表权值,\(rad\) 是一个随机数,随机是因为 Treap 为了保持其平衡,同时维护堆的性质,这样复杂度就有了保障。

2.2 合并信息

inline void pushup(int k){
	tree[k].size=tree[tree[k].l].size+tree[tree[k].r].size+tree[k].cnt;
}

比较显然,不做讲解。

2.3 左右旋

inline void zip(int &k){
	int kr=tree[k].r;
	tree[k].r=tree[kr].l;tree[kr].l=k;
	pushup(k);pushup(kr);k=kr;
}

inline void zap(int &k){
	int kl=tree[k].l;
	tree[k].l=tree[kl].r;tree[kl].r=k;
	pushup(k);pushup(kl);k=kl;
}

第一个函数是左旋,第二个是右旋。为了维护这是为了维护堆的性质,同时必须满足 BST 的性质。

img

img

通过图片不难理解上面的代码。

2.4 建立新节点

inline int New(int val){
	tree[++tail].val=val;
	tree[tail].size=tree[tail].cnt=1;
	tree[tail].rad=rand();
	return tail;
}

2.5 插入与删除

inline void insert_(int &k,int val){
	if(tail==0) k=0;
	if(!k) k=New(val);
	else if(val==tree[k].val) tree[k].cnt++;
	else if(val<tree[k].val){
		insert_(tree[k].l,val);
		if(tree[tree[k].l].rad>tree[k].rad) zap(k);
	}
	else{
		insert_(tree[k].r,val);
		if(tree[tree[k].r].rad>tree[k].rad) zip(k);
	}
	pushup(k);
}

插入实际上就是在为这个权值找一个新的位置,与此同时维护一下堆的性质,最后合并就可以了。

inline void delete_(int &k,int val){
	if(!k) return;
	if(val==tree[k].val){
		if(tree[k].cnt>1){
			tree[k].cnt--;
		}
		else if(!tree[k].l&&!tree[k].r) k=0;
		else{
			if(!tree[k].r||tree[tree[k].l].rad>tree[tree[k].r].rad){
				zap(k);
				delete_(tree[k].r,val);
			}
			else{
				zip(k);
				delete_(tree[k].l,val);
			}
		}
	}
	else val<tree[k].val?delete_(tree[k].l,val):delete_(tree[k].r,val);
	pushup(k);
}

我们希望把删除节点旋转到叶子节点上进行删除,当然进行这个操作得必须是这个点的 \(cnt\) 在删除之前为 \(1\)

。注意在旋转的过程中,还是要小心维护其堆得性质,同时因为我们的引用符号,我们直接在旋转到叶子节点之后让 \(k=0\) 就完成了删除,非常方便。

2.6 查询前驱后继,按排名找值,按值找排名

inline int getrank(int k,int val){
	if(!k) return -INF;
	if(tree[k].val==val) return tree[tree[k].l].size+1;
	if(val<tree[k].val) return getrank(tree[k].l,val);
	return getrank(tree[k].r,val)+tree[tree[k].l].size+tree[k].cnt;
}

inline int getval(int k,int rank){
	if(!k) return -INF;
	if(tree[tree[k].l].size>=rank) return getval(tree[k].l,rank);
	if(tree[tree[k].l].size+tree[k].cnt>=rank) return tree[k].val;
	return getval(tree[k].r,rank-tree[tree[k].l].size-tree[k].cnt);
}

inline int getpre(int k,int val){
	if(!k) return -INF;
	if(tree[k].val>=val) return getpre(tree[k].l,val);
	else return Max(tree[k].val,getpre(tree[k].r,val));
}

inline int getnext(int k,int val){
	if(!k) return INF;
	if(tree[k].val<=val) return getnext(tree[k].r,val);
	else return Min(tree[k].val,getnext(tree[k].l,val));
}
	

这个好理解,不做讲解。

例题

例题

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章