BST,Treap学习随笔
BST,Treap学习随笔
0 前言
学习随笔
1 BST
二叉查找树
1.1 性质
对于一个点来说
左子树中所有权值均小于当前点权值小于右子树中所有点权值

还有一个神奇的性质:
中序遍历一下 发现天然有序
上图中序遍历即为1,2,3,4,5,6,7,8,9,10,11
1.2 支持基本操作
插入(\(logn\))(最坏\(n\))
删除(\(logn\))(最坏\(n\))
找v在树上大小排第几(\(logn\))(最坏\(n\))
求第k小(\(logn\))(最坏\(n\))
求前驱(权值严格小于v的最大值)(\(logn\))(最坏\(n\))
求后继(全职严格大于v的最小值)(\(logn\))(最坏\(n\))
(因为对于同一棵树满足BST性质的形状有很多种,在一些数据下树会变成一条链,从而使操作都退化到\(O(n)\))
1.3 维护
因为Treap为BST升级版,基本操作都一样,所以直接看Treap的维护吧
2 Treap
杂交数据结构
Treap=BST(二叉查找树)+Heap(堆)
2.1 性质
既满足BST(点权)
又满足Heap(我们给点附的优先值,这里优先值是rand出来的)(可以有效避免退化成一条链的情况)

优先值满足大根堆性质(小根堆也行 只要是个堆即可)
2.2 支持操作
插入(\(logn\))
删除(\(logn\))
找v在树上大小排第几(\(logn\))
求第k小(\(logn\))
求前驱(权值严格小于v的最大值)(\(logn\))
求后继(全职严格大于v的最小值)(\(logn\))
(因为有了rand出的优先级 树在没有被神秘宇宙射线影响的情况下是不会退化到链的)
2.3 维护
2.3.1 点的信息
struct node{
int l,r,val,siz,cnt;//左节点,右节点,权值,子树大小(含自己),自己有几个
unsigned pri;//随机生成的优先值
}a[N];
2.3.2 插入
2.3.2.1 zig,zag
发现严重问题:如何同时维护两个性质
我们左思右想 决定先满足其中一个性质
先满足BST性质
首先在原树上找到应该放当前权值的位置
然后需要在不破坏当前BST性质的情况下使rand出的优先值满足Heap
于是发明出zig(右旋),zag(左旋)操作
对应不同需交换情况
/*
Y X
/ => / \
X zig A Y
/ \ /
A B B
Y X
\ => / \
X zag Y B
/ \ \
A B A
*/
发现原本的BST性质没有被破坏且X,Y父子关系还交换了
这就很像Heap中的挂在叶子节点一直往上交换的操作
size更新的时候需注意:我们在插入重复值时是直接把当前权值对应点中的cnt++
因此计算size时需要加上自己的cnt
void siz_update(int u){
if(!u)return;
a[u].siz=a[a[u].l].siz+a[a[u].r].siz+a[u].cnt;//加上自己的个数
}
void zig(int &y){//右旋 将y与y的左子树交换
//& 是为了方便直接将当前访问的y的值改变为x
int x=a[y].l;//y的左子树
a[y].l=a[x].r;//将x的右子树挂在y的左子树上
a[x].r=y;//将y挂在x的左子树上
siz_update(y);//先更新y的size(因为y现在在x下面)
siz_update(x);//再更新x的size
y=x;
}
void zag(int &y){//左旋 将y与y的右子树交换
int x=a[y].r;//y的右子树
a[y].r=a[x].l;//将x的左子树挂在y的右子树上
a[x].l=y;//将y挂在x的左子树上
siz_update(y);//更新size
siz_update(x);
y=x;
}
2.3.2.2 Insert
有了zig,zag我们就可以愉快地insert了
int add(int v){
a[++tot]={0,0,v,1,1,rnd()};
//l,r初始化为0 表示没有左节点和右节点
//val=v
//siz和cnt初始化为1
//pri优先级rand一下
return tot;
}
void Insert(int &rt,int v){//& 也是方便直接改值
if(!rt){//扫到叶子节点了
rt=add(v);
siz_update(rt);//别忘了更新大小
return;
}
if(v==a[rt].val){//有这个权值了
a[rt].cnt++;//直接加
siz_update(rt);//依旧别忘了更新
return;
}else if(v<a[rt].val){//比当前权值小 说明还要往左走
Insert(a[rt].l,v);//以左节点为根继续维护
if(a[a[rt].l].pri<a[rt].pri){//维护Heap的性质 这里只可能是左节点有问题
zig(rt);//右旋交换
}
}else if(v>a[rt].val){//同上
Insert(a[rt].r,v);
if(a[a[rt].r].pri<a[rt].pri){
zag(rt);
}
}
siz_update(rt);//别忘了更新
}
2.3.3 删除
void Erase(int &rt,int v){
if(!rt){//找到了 返回
return;
}
if(v==a[rt].val){//找到了
if(a[rt].cnt>1){//还有 直接减
a[rt].cnt--;
siz_update(rt);//别忘了更新!
return;
}else if(!a[rt].l&&!a[rt].r){//交换到了叶子节点就可以直接删除了
rt=0;//直接一直返回
return;
}else if(!a[rt].r||(a[rt].l&&a[a[rt].l].pri>a[a[rt].r].pri)){
//没有右子树 或者有左子树并且左子树优先级比右子树优先级大
zig(rt);//右旋上来
Erase(a[rt].r,v);//交换过后原本要删的点在右边
}else if(!a[rt].l||(a[rt].r&&a[a[rt].r].pri>a[a[rt].l].pri)){
//没有左子树 或者有右子树并且右子树优先级比左子树优先级大
zag(rt);//左旋上来
Erase(a[rt].l,v);//交换后原本要删的点在左边
}
}else if(v<a[rt].val){//找左子树
Erase(a[rt].l,v);
}else if(v>a[rt].val){//找右子树
Erase(a[rt].r,v);
}
siz_update(rt);//更新!!!!!!
}
2.3.4 查找排名
int Rank(int rt,int v){
if(!rt)return 1;//空树则自己就是第一
if(v==a[rt].val){//找到了
return a[a[rt].l].siz+1;//左子树中所有一定比自己小 加一就是排名
}else if(v<a[rt].val){
return Rank(a[rt].l,v);//一定在左子树中排名
}else{
return a[a[rt].l].siz+a[rt].cnt+Rank(a[rt].r,v);
//左子树大小加中间点个数再加右子树中排名
}
}
2.3.5 找第k小
int kth(int rt,int k){
if(!rt)return -1;//没找到
if(k<=a[a[rt].l].siz){//如果还没左子树大小大 说明在左子树中
return kth(a[rt].l,k);//递归左子树
}
k-=a[a[rt].l].siz;//如果比左子树大就减去
if(k<=a[rt].cnt){//剩下的比中间点个数小 说明就是中间点
return a[rt].val;
}
k-=a[rt].cnt;//还大 减去
return kth(a[rt].r,k);//递归右子树
}
2.3.6 找前驱/后继
int ask_pre(int rt,int v){//前驱
int res=-INF;//初始化为极小值
while(rt){//一直找到底才是严格小于他的最大值
if(v<=a[rt].val){//大了
rt=a[rt].l;//往左边找小的
}else{
res=a[rt].val;//够了
rt=a[rt].r;//往右边找有没有正在比v小的情况下更大的值
}
}
return res;
}
int ask_nex(int rt,int v){//后继 细节同上
int res=INF;
while(rt){
if(v>=a[rt].val){
rt=a[rt].r;
}else{
res=a[rt].val;
rt=a[rt].l;
}
}
return res;
}
2.4 模板题
附上代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=2e5+5;
const int INF=0x3f3f3f3f3f3f3f;
static unsigned seed=(unsigned)chrono::steady_clock::now().time_since_epoch().count();
unsigned rnd(){
seed^=seed<<12,seed^=seed>>5,seed^=seed<<11;
return seed;
}
struct node{
int l,r,val,siz,cnt;
unsigned pri;
}a[N];
int tot=0;
int root=0;
int n;
int add(int v){
a[++tot]={0,0,v,1,1,rnd()};
return tot;
}
void siz_update(int u){
if(!u)return;
a[u].siz=a[a[u].l].siz+a[a[u].r].siz+a[u].cnt;
}
void zig(int &y){
int x=a[y].l;
a[y].l=a[x].r;
a[x].r=y;
siz_update(y);
siz_update(x);
y=x;
}
void zag(int &y){
int x=a[y].r;
a[y].r=a[x].l;
a[x].l=y;
siz_update(y);
siz_update(x);
y=x;
}
void Insert(int &rt,int v){
if(!rt){
rt=add(v);
siz_update(rt);
return;
}
if(v==a[rt].val){
a[rt].cnt++;
siz_update(rt);
return;
}else if(v<a[rt].val){
Insert(a[rt].l,v);
if(a[a[rt].l].pri<a[rt].pri){
zig(rt);
}
}else if(v>a[rt].val){
Insert(a[rt].r,v);
if(a[a[rt].r].pri<a[rt].pri){
zag(rt);
}
}
siz_update(rt);
}
void Erase(int &rt,int v){
if(!rt){
return;
}
if(v==a[rt].val){
if(a[rt].cnt>1){
a[rt].cnt--;
siz_update(rt);
return;
}else if(!a[rt].l&&!a[rt].r){
rt=0;
return;
}else if(!a[rt].r||(a[rt].l&&a[a[rt].l].pri>a[a[rt].r].pri)){
zig(rt);
Erase(a[rt].r,v);
}else if(!a[rt].l||(a[rt].r&&a[a[rt].r].pri>a[a[rt].l].pri)){
zag(rt);
Erase(a[rt].l,v);
}
}else if(v<a[rt].val){
Erase(a[rt].l,v);
}else if(v>a[rt].val){
Erase(a[rt].r,v);
}
siz_update(rt);
}
int Rank(int rt,int v){
if(!rt)return 1;
if(v==a[rt].val){
return a[a[rt].l].siz+1;
}else if(v<a[rt].val){
return Rank(a[rt].l,v);
}else{
return a[a[rt].l].siz+a[rt].cnt+Rank(a[rt].r,v);
}
}
int kth(int rt,int k){
if(!rt)return -1;
if(k<=a[a[rt].l].siz){
return kth(a[rt].l,k);
}
k-=a[a[rt].l].siz;
if(k<=a[rt].cnt){
return a[rt].val;
}
k-=a[rt].cnt;
return kth(a[rt].r,k);
}
int ask_pre(int rt,int v){
int res=-INF;
while(rt){
if(v<=a[rt].val){
rt=a[rt].l;
}else{
res=a[rt].val;
rt=a[rt].r;
}
}
return res;
}
int ask_nex(int rt,int v){
int res=INF;
while(rt){
if(v>=a[rt].val){
rt=a[rt].r;
}else{
res=a[rt].val;
rt=a[rt].l;
}
}
return res;
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>n;
while(n--){
int op,x;
cin>>op>>x;
if(op==1){
Insert(root,x);
}else if(op==2){
Erase(root,x);
}else if(op==3){
cout<<Rank(root,x)<<endl;
}else if(op==4){
cout<<kth(root,x)<<endl;
}else if(op==5){
cout<<ask_pre(root,x)<<endl;
}else if(op==6){
cout<<ask_nex(root,x)<<endl;
}
}
return 0;
}
完结撒花🎉🎉🎉

浙公网安备 33010602011771号