首页 > 其他分享 >【模板】普通平衡树

【模板】普通平衡树

时间:2024-08-29 11:16:24浏览次数:4  
标签:val int res splay 普通 平衡 root 模板 Size

具体讲解见OI-wiki(他的左旋右旋跟蓝书的有点不一样,按照蓝书的理解,代码见下),下面是一些补充

拓展:

1.将一个序列插入到\(x\)的后面:找到\(x\)的后继\(y\),先将\(x\)伸展到根,再将\(y\)伸展到\(x\)的右子树,此时由于\(y\)是\(x\)的后继所以\(y\)的左儿子为空;对序列建出一棵二叉树(可以像线段树那个样子建,也可以直接建成一条链,反正是二叉树就可以了,但是要保证中序遍历),令\(y\)的左儿子为这棵Splay树的根节点即可

2.将一个序列删除:设删除序列为\([l,r]\),则将\(l-1\)伸展到根节点,再将\(r+1\)伸展到根节点的右儿子,令\(r+1\)的左儿子为空即可

代码见下

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=100010;
struct treap
{
    int l,r;
    int cnt,Size;
    int val;
}a[N];
int tot,n,INF=0x7fffffff,root,fa[N];
int New(int val)
{
    a[++tot].val=val;
    a[tot].cnt=a[tot].Size=1;
    return tot;
}
void update(int p)
{
    a[p].Size=a[a[p].l].Size+a[a[p].r].Size+a[p].cnt;
}
void build()
{
    New(INF),New(-INF);
    a[1].l=2,fa[2]=1;//记录每个点的父亲,这样方便splay操作 
    root=1;
    update(1);
}
pair<int,int> getrank(int p,int val)//first是答案rank,second是得到答案的点,最后会进行splay 
{
    if(!p) return make_pair(0,-1);//-1表示没找到,不进行splay 
    if(val==a[p].val) return make_pair(a[a[p].l].Size,p);
    pair<int,int> res;
    if(val<a[p].val) return getrank(a[p].l,val);
    res=getrank(a[p].r,val);
    return make_pair(a[a[p].l].Size+a[p].cnt+res.first,res.second);
}
pair<int,int> getval(int p,int rank)//first是答案val,second是得到答案的点,最后会进行splay 
{
    if(rank<=a[a[p].l].Size) return getval(a[p].l,rank);
    if(rank<=a[a[p].l].Size+a[p].cnt) return make_pair(a[p].val,p);
    return getval(a[p].r,rank-(a[a[p].l].Size+a[p].cnt));
}
int get(int x)//判断x是其父亲的左儿子还是右儿子 
{
	if(a[fa[x]].l==x) return 0;//左儿子 
	else return 1;//右儿子 
}
void zig(int p)//右旋(注意这里没有引用了) 
{
    int q=a[p].l,flag=get(p);
    a[p].l=a[q].r;
    if(a[q].r) fa[a[q].r]=p;
	a[q].r=p,fa[q]=fa[p],fa[p]=q;
    if(fa[q]) 
    {
    	if(!flag) a[fa[q]].l=q;
    	else a[fa[q]].r=q;
	}
    update(p),update(q);
}
void zag(int p)//左旋 
{
    int q=a[p].r,flag=get(p);
    a[p].r=a[q].l;
    if(a[q].l) fa[a[q].l]=p;
	a[q].l=p,fa[q]=fa[p],fa[p]=q;
    if(fa[q]) 
    {
    	if(!flag) a[fa[q]].l=q;
    	else a[fa[q]].r=q;
	}
    update(p),update(q);
}
void splay(int x)
{
	for(int f=fa[x];f;f=fa[x])
	if(fa[f])
	{
		bool sonx=get(x),sonf=get(f);
		if(sonx==sonf)//一条链的情况 
		{
			if(!sonx) zig(fa[f]),zig(f);
			else zag(fa[f]),zag(f);
		}
		else//中间有折点的情况 
		{
			if(!sonx) zig(f),zag(fa[x]);
			else zag(f),zig(fa[x]);
		}
	}
	else
	{
		bool sonx=get(x);
		if(!sonx) zig(f);
		else zag(f);
	}
	root=x;
}
int insert(int &p,int f,int val)//插入操作,f为父亲,返回值为splay的点 
{
    if(!p)
    {
        p=New(val);
        fa[p]=f;//这个别忘 
        return p;
    }
    if(val==a[p].val)
    {
        a[p].cnt++,a[p].Size++;
        return p;
    }
    int res;
    if(val<a[p].val) res=insert(a[p].l,p,val);
    else res=insert(a[p].r,p,val);
    update(p);
    return res;
}
int getpre(int val)//找前驱 
{
    int ans=2;
    int p=root;
    while(p)
    {
        if(val==a[p].val)
        {
            if(a[p].l)
            {
                p=a[p].l;
                while(a[p].r)p=a[p].r;
                ans=p;
            }
            break;
        }
        if(a[p].val<val&&a[p].val>a[ans].val) ans=p;
        p=val<a[p].val?a[p].l:a[p].r;
    }
    splay(ans);//这里可以直接splay,只会改变树的结构将ans变成root,但是不会改变ans存储的信息 
    return a[ans].val;
}
int getnext(int val)
{
    int ans=1;
    int p=root;
    while(p)
    {
        if(val==a[p].val)
        {
            if(a[p].r)
            {
                p=a[p].r;
                while(a[p].l)p=a[p].l;
                ans=p;
            }
            break;
        }
        if(a[p].val>val&&a[p].val<a[ans].val) ans=p;
        p=val<a[p].val?a[p].l:a[p].r;
    }
    splay(ans);//同上 
    return a[ans].val;
}
void remove(int val)
{
    splay(getrank(root,val).second);//将删除的点splay到根 
    if(a[root].cnt>1)
    {
    	a[root].cnt--,a[root].Size--;
    	return;
	}
	int L=a[root].l,R=a[root].r;
	while(a[L].r) L=a[L].r;
	//此时L一定没有右子树,而且是从a[root].l一直往右走得到的 
	splay(L);//此时L变成了根,原来的根为L的右子树,R为原来的根的子树 
	fa[R]=L,a[L].r=R;//由上面的分析直接O(1)修改没问题 
	update(L); 
}
int main()
{
    build();
    scanf("%d",&n);
    while(n--)
    {
        int opt,x,y;
        pair<int,int> res;
        scanf("%d%d",&opt,&x);
        switch(opt)
        {
            case 1:
                y=insert(root,0,x);
                splay(y);
                break;
            case 2:
                remove(x);
                break;
            case 3:
            	res=getrank(root,x);
                printf("%d\n",res.first);
                if(res.second!=-1) splay(res.second);
                break;
            case 4:
            	res=getval(root,x+1);
                printf("%d\n",res.first);
                splay(res.second);
                break;
            case 5:
                printf("%d\n",getpre(x));
                break;
            case 6:
                printf("%d\n",getnext(x));
                break;
        }
    }
    return 0;
}

标签:val,int,res,splay,普通,平衡,root,模板,Size
From: https://www.cnblogs.com/dingxingdi/p/18386287

相关文章

  • [学习笔记] Splay & Treap 平衡树 - 数据结构
    [学习笔记]Splay&Treap平衡树-数据结构Splay树又名伸展树,一种平衡二叉查找树,通过\(\text{Splay}\)操作不断把节点旋到根节点来维护整颗树的平衡。说人话,很玄学的玩意,复杂度是单log级别的。为啥是单log,科学的解释请移步OI-WIKI。不科学的解释就是,通过不断\(\tex......
  • 13.JS学习篇-ES6 React 项目模板
    1.项目能力支持1.项目初始化脚手架1.前端编码规范工程化(lint工具、NodeCLI等)2.用工具提升项目的编码规范,如:eslint、stylelint、commitlint、markdownlint、husky等3.工具对于JavaScript、Typescript、React、Vue等不同类型的前端项目下的标准的语法限制;2.相关基础功能......
  • NETCORE下用SKIT类库发送微信模板消息
    NETCORE下用SKIT类库发送微信模板消息 //测试发送模板消息-微信公众号//https://developers.weixin.qq.com/doc/offiaccount/Message_Management/Template_Message_Interface.html#5publicasyncTask<IActionResult>Ceshi(intid,stringopenid)......
  • YOLOv9改进策略【损失函数篇】| Slide Loss,解决简单样本和困难样本之间的不平衡问题
    一、本文介绍本文记录的是改进YOLOv9的损失函数,将其替换成SlideLoss,并详细说明了优化原因,注意事项等。SlideLoss函数可以有效地解决样本不平衡问题,为困难样本赋予更高的权重,使模型在训练过程中更加关注困难样本。若是在自己的数据集中发现容易样本的数量非常大,而困难样本......
  • halcon的模板匹配
    halcon的三种模板匹配方法总结halcon有三种模板匹配方法:即Component-Based、Gray-Value-Based、Shaped_based,分别是基于组件(或成分、元素)的匹配,基于灰度值的匹配和基于形状的匹配,此外还有变形匹配和三维模型匹配也是分属于前面的大类本文只对形状匹配做简要说明和补充:Shape_Ba......
  • 深度解析HarmonyOS SDK实况窗服务源码,Get不同场景下的多种模板
    HarmonyOSSDK实况窗服务(LiveViewKit)作为一个实时呈现应用服务信息变化的小窗口,遍布于设备的各个使用界面,它的魅力在于将复杂的应用场景信息简洁提炼并实时刷新,在不影响当前其他应用操作的情况下,时刻向用户展示最新的信息动态,用户也可以点击实况窗卡片或胶囊进入应用落地页查看......
  • 【数据结构】【模板】李超线段树
    李超线段树定义可以看看洛谷的模板题目:作用优化动态规划,如果可以将一个动态规划的转移式子转化为\(y=kx+b\)的形式,那么我们可以边转移边将\(y=kx+b\)这条线段放入李超线段树,然后在下次转移时,直接调用下次计算出来的\(x\)位置上的最大值或最小值。(结合题目理解......
  • 【Leetcode_Hot100】普通数组
    普通数组53.最大子数组和56.合并区间189.轮转数组238.除自身以外数组的乘积41.缺失的第一个正数53.最大子数组和方法一:暴力解依次遍历数组中的每个子数组,进而判断res的最大值超时classSolution{publicintmaxSubArray(int[]nums){intres=0;......
  • D11 kubernetes yaml模板与示例
    》 kubernetes资源的创建、更新、删除等操作均可以使用json或者yaml文件进行操作,更新和删除可以依赖之前的文件进行更改。但是资源清单文件就没那么容易了,k8s的配置项实在是太多,稍微不注意就会犯错。要写好一个yaml文件,需要了解yaml文件的语法,需要整我k8s的各种配置。本文按照k8s......
  • 有趣的C++模板代码
    1#include<iostream>2template<typename...Ts>3structCNAny{4staticboolDo(inti){5return(Ts::Do(i)||...);6}7};89template<typename...Ts>10structCNAll{11staticboolDo(inti){......