首页 > 其他分享 >FHQ Treap

FHQ Treap

时间:2024-04-25 13:22:32浏览次数:11  
标签:val merge int fhq Treap root now FHQ

P3369 【模板】普通平衡树

前言:

平衡树是一种二叉搜索树,通过一些方法来做到快速维护单点或区间信息和快速查询单点或区间信息,其中包括排名、前驱等等。在 \(\rm STL\) 库中虽有实现,但是由于封装的太好以及是可持久化数据结构的基础,还是需要学习的。

FHQ Treap:

FHQ Treap 是一种不带旋操作的平衡树,使用分裂和合并为基础操作,以此来维护树的平衡性。

节点操作:

节点定义:

这没啥好讲的。

struct node{
	int ls,rs;//左右儿子
	int val,key;// 值与键值
	int siz;// 子树大小
}fhq[N];
int root,cnt;// 根节点编号和节点数量

新建节点:

inline int newnode(int val){// 返回新节点编号
	fhq[++cnt].val=val;
	fhq[cnt].key=rnd();//这里用的是 std::mt19937 rnd(114514)
	fhq[cnt].siz=1;
	return cnt;
}

更新节点信息:

inline void update(int o){
	fhq[o].siz=fhq[fhq[o].ls].siz+fhq[fhq[o].rs].siz+1;
}

基础操作:

分裂(split):

将树以阈值 \(val\) 拆分成 \(\le val\) 和 \(> val\) 的两棵树。

inline void split(int now,int val,int& x,int& y){// now 当前节点,val 是阈值,x 是 <=val 的树,y 是 >val 的树(都取根节点来表示)
	if(!now)x=y=0;//空树
	else{
		if(fhq[now].val<=val){// 假如当前节点的值小于等于阈值,则左子树中的所有节点都是 x 树 的,右子树则需要递归判断
			x=now;
			split(fhq[now].rs,val,fhq[now].rs,y);
		}
		else{// 否则此时的右子树都属于 y 树,左子树递归判断
			y=now;
			split(fhq[now].ls,val,x,fhq[now].ls);
		}
		update(now);// 记得更新节点信息
	}
}

合并(merge):

合并两棵树为一棵树,而且前面的树一定要是在左边的。

inline int merge(int x,int y){// 返回根节点编号
	if(!x||!y)return x+y;// 假如两树有一树为空或是两树都为空,则返回其中不空的一棵或是返回空
	if(fhq[x].key>fhq[y].key){// 假如 x 树根节点的键值大于 y 树根节点的,此时 x 树根节点一定在 y 树根节点上方。
        			  //也就是 x 树根节点就是合并后两棵树的根节点,所以此时让右子树再去跟 y 树合并。
		fhq[x].rs=merge(fhq[x].rs,y);
		update(x);// 更新节点信息
		return x;
	}
	else{// 否则同理
		fhq[y].ls=merge(x,fhq[y].ls);
		update(y);
		return y;
	}
}

更新操作:

插入(insert):

插入一个值为 \(val\) 的新节点。

int x,y,z;
inline void ins(int val){
	split(root,val,x,y);//先将原树以 val 为阈值割分成两部分 x,y
	root=merge(merge(x,newnode(val)),y);//此时我们按 x,new,y 的顺序合并即可
}

删除(delete):

删除一个值为 \(val\) 的节点。

inline void del(int val){
	split(root,val,x,z);// 先将原树树割分成 <=val(x) 和 >val(z)
	split(x,val-1,x,y);// 再将 x 树割分成 <val(x) 和 =val(y) 
	y=merge(fhq[y].ls,fhq[y].rs);//将 y 树中的根节点删除
	root=merge(merge(x,y),z);// 合并回来
}

查询一个数的排名(getrank):

查询一个数 \(val\) 在序列中是第几大的数。

inline int getrank(int val){
	split(root,val-1,x,y);//把 val-1 的部分割出来
	int rank=fhq[x].siz+1;// 此时直接求就好
	root=merge(x,y);// 合并回来
	return rank;
}

查询排名所对应的值(getnum):

查询排名 \(rank\) 所对应的值。

inline int getnum(int rank){
	int now=root;
	while(now){
		if(fhq[fhq[now].ls].siz+1==rank)break;// 假如这个节点就是,直接 break 掉
		if(fhq[fhq[now].ls].siz<rank){// 左子树的节点不够
			rank-=(fhq[fhq[now].ls].siz+1);//减掉左子树的节点数量,即为需要在右子树中找第几大的数
			now=fhq[now].rs;// 往右走
		}
		else now=fhq[now].ls;// 左子树节点数量还绰绰有余时,就往左子树走
	}
	return fhq[now].val;
}

查询一个数的前驱(pre):

查询在比值 \(val\) 小 的数集合中最大的数。

inline int pre(int val){
	split(root,val-1,x,y);// 把树割成 <val(x) 和 >=val(y) 两树
	int now=x;
	while(fhq[now].rs)now=fhq[now].rs;// 在 x 树中最大的数就是最右边的数
	root=merge(x,y);// 合并回来
	return fhq[now].val;
}

查询一个数的后继(nxt):

查询在比值 \(val\) 大的数集合中最小的数。

inline int nxt(int val){// 同理
	split(root,val,x,y);
	int now=y;
	while(fhq[now].ls)now=fhq[now].ls;
	root=merge(x,y);
	return fhq[now].val;
}

至此我们顺利的完成了所有操作。

code:

#include<bits/stdc++.h>
#define int long long
using namespace std;

const int N=1e5+10;
std::mt19937 rnd(114514);
struct FHQ_Treap{
	struct node{
		int ls,rs;
		int val,key;
		int siz;
	}fhq[N];
	int root,cnt;
	inline int newnode(int val){
		fhq[++cnt].val=val;
		fhq[cnt].key=rnd();
		fhq[cnt].siz=1;
		return cnt;
	}
	inline void update(int o){
		fhq[o].siz=fhq[fhq[o].ls].siz+fhq[fhq[o].rs].siz+1;
	}
	inline void split(int now,int val,int& x,int& y){
		if(!now)x=y=0;
		else{
			if(fhq[now].val<=val){
				x=now;
				split(fhq[now].rs,val,fhq[now].rs,y);
			}
			else{
				y=now;
				split(fhq[now].ls,val,x,fhq[now].ls);
			}
			update(now);
		}
	}
	inline int merge(int x,int y){
		if(!x||!y)return x+y;
		if(fhq[x].key>fhq[y].key){
			fhq[x].rs=merge(fhq[x].rs,y);
			update(x);
			return x;
		}
		else{
			fhq[y].ls=merge(x,fhq[y].ls);
			update(y);
			return y;
		}
	}
	int x,y,z;
	inline void ins(int val){
		split(root,val,x,y);
		root=merge(merge(x,newnode(val)),y);
	}
	inline void del(int val){
		split(root,val,x,z);
		split(x,val-1,x,y);
		y=merge(fhq[y].ls,fhq[y].rs);
		root=merge(merge(x,y),z);
	}
	inline int getrank(int val){
		split(root,val-1,x,y);
		int rank=fhq[x].siz+1;
		root=merge(x,y);
		return rank;
	}
	inline int getnum(int rank){
		int now=root;
		while(now){
			if(fhq[fhq[now].ls].siz+1==rank)break;
			if(fhq[fhq[now].ls].siz<rank){
				rank-=(fhq[fhq[now].ls].siz+1);
				now=fhq[now].rs;
			}
			else now=fhq[now].ls;
		}
		return fhq[now].val;
	}
	inline int pre(int val){
		split(root,val-1,x,y);
		int now=x;
		while(fhq[now].rs)now=fhq[now].rs;
		root=merge(x,y);
		return fhq[now].val;
	}
	inline int nxt(int val){
		split(root,val,x,y);
		int now=y;
		while(fhq[now].ls)now=fhq[now].ls;
		root=merge(x,y);
		return fhq[now].val;
	}
}tree;

signed main(){
	std::ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	int T;cin>>T;
	while(T--){
		int opt,val;
		cin>>opt>>val;
		if(opt==1)tree.ins(val);
		if(opt==2)tree.del(val);
		if(opt==3)cout<<tree.getrank(val)<<"\n";
		if(opt==4)cout<<tree.getnum(val)<<"\n";
		if(opt==5)cout<<tree.pre(val)<<"\n";
		if(opt==6)cout<<tree.nxt(val)<<"\n";
	}

	return 0;
}

标签:val,merge,int,fhq,Treap,root,now,FHQ
From: https://www.cnblogs.com/little-corn/p/18157469

相关文章

  • FHQ
    FHQ平衡树实质:FHQ平衡树又称无旋Treap。(即通过\(Split与Merge\)来替代旋转。)本质上还是一颗二分查找树,再利用随机数来维护相对平衡的树形结构。实现:\(Node\):对于每个节点维护\(siz,pri,key,ls,rs\)。structnode{intls,rs,pri,siz,key;}\(Split\):voidsplit(inti,......
  • 二叉查找树/堆 /Treap/Spaly树串联分析
    二叉查找树(BST)二叉查找树:中序遍历是一个递增的序列。父节点的左子树的所有结点都比父节点小,父节点右子树的结点比父节点都大。在一颗随机构造的BST上,查找一个元素的时间复杂度位O(logn),但是如果我们有序的插入结点,那么BST的高度将位N,时间复杂度位O(n)。importjava.util.Sc......
  • FHQ-treap学习笔记
    平衡树,即平衡二叉搜索树。二叉搜索树(BST),它或者是一棵空树,或者是具有下列性质的二叉树:若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;它的左、右子树也分别为二叉排序树。(from百度百科)而在使用这种......
  • Treap 树
    1.概念Treap树是维护二叉查找树平衡的一种方法。它的核心思想是给每个结点设置一个随机的优先级,使它成为一个堆,即父亲的优先级一定小于(或大于)孩子的优先级。大于则为大根堆,小于则为小根堆。本节使用的是小根堆。这样就可以实现概率上的平衡。下图是一棵Treap树的建树方法。......
  • Treap(平衡树)
    Treap前置芝士二叉搜索树(BST),见BST。平衡二叉树(AVL)。先来介绍一下平衡二叉树。背景BST出现以后,人们很快发现一个问题,当其维护一个有序序列时,很可能会退化成链。如图:这样的话,原来\(O(\log{n})\)的复杂度就退化为\(O(n)\),这是我们无法接受的,于是平衡二叉树横空出世......
  • Treap
    前置:BST\(Treap=BST+heap\),通过额外维护堆的性质来避免退化成链的问题。与\(BST\)中结构释义相同的部分不做解释。\(priority\)表示优先级,即该节点在小根堆中的值,\(child[0]\)表示左孩子,\(child[1]\)表示右孩子。\(public\)中,\(empty\):时间复杂度\(O(1)\)。其余操作时间复杂......
  • 平衡树(无旋Treap,范浩强树)学习笔记
    平衡树:YYDS以下是常见的平衡树/要用平衡树实现的算法:Treap(有旋/无旋)Splay树WBLT(WeightBalancedLeafyTree,重量平衡线段树)SBT(SizeBalancedTree,陈启峰树)AVL树B树、B+树笛卡尔树红黑树、左偏红黑树、AA树替罪羊树\(\to\)K-DTree(k-DimensionTree)LT(LeafyTree,平......
  • Treap 学习笔记
    二叉查找树二叉查找树是一棵有点权的二叉树,具有以下几个特征:左孩子的权值小于父亲的权值右孩子的权值大于父亲的权值中序遍历及从小到大排序二叉查找树支持以下几个操作:插入一个数删除一个数找一个数的前驱找一个数的后继询问一个数的排名询问排第几名的数二叉查......
  • Treap
    概述FHQTreap基于Treap发明的“无旋Treap”,代码短,易理解且速度快(在OI算是很优秀的算法了)。FHQTreap核心函数只有两个,分别是分裂和合并。字面意思,就是将某一棵二叉查找树按某种要求分裂成两个或将两个合并成一个。实现变量维护变量名功能root记录所维护......
  • 算法笔记(2)FHQtreap
    原发布于我的个人博客前言FHQtreap绝对是平衡树里最好写,最实用的,他几乎能做所有splay或其它平衡树能做的事,还能可持久化!这篇文章将会介绍FHQtreap的基本操作和维护区间的操作,并附上例题。基本操作FHQtreap的基本操作只有两个,分裂和合并。有些读者可能会问,分裂和合并和平衡树......