【平衡树】splay伸展树

一.定义

 伸展树(Splay Tree)是一种自调整二叉搜索树,它通过不断进行伸展(splay)操作,将最近访问的节点移动到树的根节点,以提高对这些节点的访问效率。伸展树的主要特点是在插入、查找和删除操作时,都会执行伸展操作,使得最近访问的节点位于根节点,从而实现了一种局部性原理,即频繁访问的节点更容易被快速访问。

伸展树的基本伸展操作有三种:

  1. 伸展到根(Splay to Root):将某个节点x伸展到树的根节点,通过一系列旋转操作实现,使得x成为新的根节点。

  2. 伸展到左子树的最右节点(Splay to Right Child's Leftmost Descendant):将某个节点x伸展到其左子树的最右节点,同样通过一系列旋转操作实现。

  3. 伸展到右子树的最左节点(Splay to Left Child's Rightmost Descendant):将某个节点x伸展到其右子树的最左节点,同样通过一系列旋转操作实现。

伸展树的操作包括插入、查找和删除。在每次操作之后,都会对相关的节点进行伸展,以保持树的平衡性和局部性原理。这种自调整性质使得伸展树在一般情况下表现出良好的性能,但最坏情况下的性能可能较差,因为它不保证平衡。

总之,伸展树是一种简单而有效的自平衡二叉搜索树,适用于需要频繁访问最近使用节点的场景。在实际应用中,可以根据具体需求选择合适的自平衡数据结构,如AVL树、红黑树等。


二.数据存储方式  && main函数

struct node{int val,cnt; //它的值 ,值有几个 int fa,son[2]; // 它的father和它的两个sonint siz; //它的子树个数 (即<=它的数有几个) 
}tree[maxn];
int main(){insert(-2147483647);insert(+2147483647);scanf("%d",&n);int ops,x;for(int i=1;i<=n;i++){scanf("%d%d",&ops,&x);if(ops==1) insert(x);else if(ops==2) del(x);else if(ops==3) Find(x),printf("%d\n",tree[tree[root].son[0]].siz);else if(ops==4) printf("%d\n",kth(x+1));else if(ops==5) printf("%d\n",tree[pre(x)].val);else printf("%d\n",tree[nxt(x)].val);}return 0;
}

三. insert

void insert(int x){int u=root,fa=0;while(u && tree[u].val!=x){fa=u;u=tree[u].son[x>tree[u].val];}//已经有这个数字 if(u){tree[u].cnt++;}else{u=++cnt;if(fa){tree[fa].son[x>tree[fa].val]=u;}tree[u].fa=fa;tree[u].son[0]=tree[u].son[1]=0;tree[u].val=x;tree[u].cnt=tree[u].siz=1;} splay(u,0);
}


四.splay

  • 若共线要先转一下父节点,再转x
  • 不共线,转两下x即可
  • 重复上面操作,直到到达目标
    void splay(int x,int goal){while(tree[x].fa!=goal){int fa=tree[x].fa,gfa=tree[fa].fa;if(gfa!=goal){//同ture意思是都为左孩子,异或为0;同false都为右孩子,异或为0 ((tree[fa].son[0]==x)^(tree[gfa].son[0]==fa))==0 ? rotate(fa) : rotate(x);}	rotate(x);}if(goal==0) root=x;
    }


五.rotate

注意:这边使用了异或和判断孩子的功能,固左转和右转通用

void updata(int x){tree[x].siz=tree[ tree[x].son[0] ].siz+tree[ tree[x].son[1] ].siz+tree[x].cnt;
}
void rotate(int x){//预处理 int fa=tree[x].fa,gfa=tree[fa].fa;int k=(tree[fa].son[1]==x); //左右孩子 //继承fa为gfa的孩子tree[gfa].son[ tree[gfa].son[1]==fa ] = x;tree[x].fa=gfa;//调整fa的son,即找人代替x为fa的k儿子 tree[fa].son[k]=tree[x].son[k^1];tree[ tree[x].son[k^1] ].fa = fa;//风水轮流转,爸爸成儿子tree[x].son[k^1]=fa;tree[fa].fa=x; //fa和x子树有变动,要更新 updata(fa);updata(x);
}

六.前驱后继

int pre(int x){Find(x);if(tree[root].val<x) return root; //特判//左子树中找最右的点int u=tree[root].son[0];while(tree[u].son[1]) u=tree[u].son[1];return u; 
}
int nxt(int x){Find(x);if(tree[root].val>x) return root; //特判 //右子树中找最左的点int u=tree[root].son[1];while(tree[u].son[0]) u=tree[u].son[0];return u; 	
}

七.delete

void del(int x){int p=pre(x),s=nxt(x);splay(p,0);splay(s,p);int del=tree[s].son[0];if(tree[del].cnt>1){tree[del].cnt--;//存在多个这个数字,直接减去一个 splay(del,0);}else{tree[s].son[0]=0;//清除掉节点 }   
}

八.查排名

int kth(int x){int u=root;if(tree[u].siz<x) return false;while(1){int y=tree[u].son[0];if(x>tree[y].siz+tree[u].cnt){	//排在u的后面 x-=tree[y].siz+tree[u].cnt;u=tree[u].son[1];}else if(tree[y].siz>=x){  //在u的前面 u=y;}else{return tree[u].val;}}
}

九.查排第几

将其变成根,看看左子树有多少个即可


十.AC代码

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;
int n;
int root,cnt;
struct node{int val,cnt; //它的值 ,值有几个 int fa,son[2]; // 它的father和它的两个sonint siz; //它的子树个数 (即<=它的数有几个) 
}tree[maxn];
void updata(int x){tree[x].siz=tree[ tree[x].son[0] ].siz+tree[ tree[x].son[1] ].siz+tree[x].cnt;
}
void rotate(int x){//预处理 int fa=tree[x].fa,gfa=tree[fa].fa;int k=(tree[fa].son[1]==x); //左右孩子 //继承fa为gfa的孩子tree[gfa].son[ tree[gfa].son[1]==fa ] = x;tree[x].fa=gfa;//调整fa的son,即找人代替x为fa的k儿子 tree[fa].son[k]=tree[x].son[k^1];tree[ tree[x].son[k^1] ].fa = fa;//风水轮流转,爸爸成儿子tree[x].son[k^1]=fa;tree[fa].fa=x; //fa和x子树有变动,要更新 updata(fa);updata(x);
}
void splay(int x,int goal){while(tree[x].fa!=goal){int fa=tree[x].fa,gfa=tree[fa].fa;if(gfa!=goal){//同ture意思是都为左孩子,异或为0;同false都为右孩子,异或为0 ((tree[fa].son[0]==x)^(tree[gfa].son[0]==fa))==0 ? rotate(fa) : rotate(x);}	rotate(x);}if(goal==0) root=x;
}
void insert(int x){int u=root,fa=0;while(u && tree[u].val!=x){fa=u;u=tree[u].son[x>tree[u].val];}//已经有这个数字 if(u){tree[u].cnt++;}else{u=++cnt;if(fa){tree[fa].son[x>tree[fa].val]=u;}tree[u].fa=fa;tree[u].son[0]=tree[u].son[1]=0;tree[u].val=x;tree[u].cnt=tree[u].siz=1;} splay(u,0);
}
void Find(int x){int u=root;if(!u) return;//若x不存在,则u一定有误差,但在pre or nxt函数中已经特判了 while(tree[u].son[x>tree[u].val] && x!=tree[u].val){u=tree[u].son[x>tree[u].val];}splay(u,0);
}
int pre(int x){Find(x);if(tree[root].val<x) return root; //特判//左子树中找最右的点int u=tree[root].son[0];while(tree[u].son[1]) u=tree[u].son[1];return u; 
}
int nxt(int x){Find(x);if(tree[root].val>x) return root; //特判 //右子树中找最左的点int u=tree[root].son[1];while(tree[u].son[0]) u=tree[u].son[0];return u; 	
}
void del(int x){int p=pre(x),s=nxt(x);splay(p,0);splay(s,p);int del=tree[s].son[0];if(tree[del].cnt>1){tree[del].cnt--;//存在多个这个数字,直接减去一个 splay(del,0);}else{tree[s].son[0]=0;//清除掉节点 }   
}
int kth(int x){int u=root;if(tree[u].siz<x) return false;while(1){int y=tree[u].son[0];if(x>tree[y].siz+tree[u].cnt){	//排在u的后面 x-=tree[y].siz+tree[u].cnt;u=tree[u].son[1];}else if(tree[y].siz>=x){  //在u的前面 u=y;}else{return tree[u].val;}}
}
int main(){insert(-2147483647);insert(+2147483647);scanf("%d",&n);int ops,x;for(int i=1;i<=n;i++){scanf("%d%d",&ops,&x);if(ops==1) insert(x);else if(ops==2) del(x);else if(ops==3) Find(x),printf("%d\n",tree[tree[root].son[0]].siz);else if(ops==4) printf("%d\n",kth(x+1));else if(ops==5) printf("%d\n",tree[pre(x)].val);else printf("%d\n",tree[nxt(x)].val);}return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/149141.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

【动手学深度学习-Pytorch版】Transformer代码总结

本文是纯纯的撸代码讲解&#xff0c;没有任何Transformer的基础内容~ 是从0榨干Transformer代码系列&#xff0c;借用的是李沐老师上课时讲解的代码。 本文是根据每个模块的实现过程来进行讲解的。如果您想获取关于Transformer具体的实现细节&#xff08;不含代码&#xff09;可…

MySQL的复合查询

文章目录 1. 多表查询2. 自连接3. 子查询3.1 单行子查询3.2 多行单列子查询3.3 单行多列子查询3.4 在from子句中使用子查询 4. 合并查询4.1 union all4.2 union 5. 内连接6. 外连接6.1 左外连接6.2 右外连接 1. 多表查询 前面我们讲解的mysql表的查询都是对一张表进行查询&…

哨兵(Sentinel-1、2)数据下载

哨兵&#xff08;Sentinel-1、2&#xff09;数据下载 一、登陆欧空局网站 二、检索 先下载2号为光学数据 分为S2A和S2B&#xff0c;产品种类有1C和2A&#xff0c;区别就是2A是做好大气校正的影像&#xff0c;当然数量也会少一些&#xff0c;云量检索条件中记得要按格式&#x…

Mind Map:大语言模型中的知识图谱提示激发思维图10.1+10.2

知识图谱提示激发思维图 摘要介绍相关工作方法第一步&#xff1a;证据图挖掘第二步&#xff1a;证据图聚合第三步&#xff1a;LLM Mind Map推理 实验实验设置医学问答长对话问题使用KG的部分知识生成深入分析 总结 摘要 LLM通常在吸收新知识的能力、generation of hallucinati…

一键AI高清换脸——基于InsightFace、CodeFormer实现高清换脸与验证换脸后效果能否通过人脸比对、人脸识别算法

前言 1、项目简介 AI换脸是指利用基于深度学习和计算机视觉来替换或合成图像或视频中的人脸。可以将一个人的脸替换为另一个人的脸,或者将一个人的表情合成到另一个人的照片或视频中。算法常常被用在娱乐目上,例如在社交媒体上创建有趣的照片或视频,也有用于电影制作、特效…

Qt model/view 理解01

在 Qt 中对数据处理主要有两种方式&#xff1a;1&#xff09;直接对包含数据的的数据项 item 进行操作&#xff0c;这种方法简单、易操作&#xff0c;现实方式单一的缺点&#xff0c;特别是对于大数据或在不同位置重复出现的数据必须依次对其进行操作&#xff0c;如果现实方式改…

44 二叉搜索树中第K个小的元素

二叉搜索树中第K个小的元素 题解1 中序遍历题解2 AVL&#xff08;手撕平衡二叉树&#xff1a;谢谢力扣官方&#xff09; 给定一个二叉搜索树的根节点 root &#xff0c;和一个整数 k &#xff0c;请你设计一个算法查找其中第 k 个最小元素&#xff08;从 1 开始计数&#xf…

再来介绍另一个binlog文件解析的第三方工具my2sql

看腻了文字就来听听视频演示吧&#xff1a;https://www.bilibili.com/video/BV1rp4y1w74B/ github项目&#xff1a;https://github.com/liuhr/my2sql gitee链接&#xff1a;https://gitee.com/mirrors/my2sql my2sql go版MySQL binlog解析工具&#xff0c;通过解析MySQL bin…

Maven 中引用其他项目jar包出现BOOT-INF问题

问题 在B项目中引入A项目的类&#xff0c;但是发现怎么也引入不进来 A项目打包之后&#xff0c;想在B项目中引用jar 在B项目中发现类文件无法引用 参考网上进行清缓存等一系列操作都没有解决。 最后发现引用的jar包中包含BOOT-INF&#xff0c; 然后去A项目中查找&#xff…

基于回溯搜索优化的BP神经网络(分类应用) - 附代码

基于回溯搜索优化的BP神经网络&#xff08;分类应用&#xff09; - 附代码 文章目录 基于回溯搜索优化的BP神经网络&#xff08;分类应用&#xff09; - 附代码1.鸢尾花iris数据介绍2.数据集整理3.回溯搜索优化BP神经网络3.1 BP神经网络参数设置3.2 回溯搜索算法应用 4.测试结果…

基于MFC和OpenCV实现人脸识别

基于MFC和OpenCV实现人脸识别 文章目录 基于MFC和OpenCV实现人脸识别1. 项目说明1. 创建项目2. 启动窗口3. 登录窗口-添加窗口、从启动窗口跳转4. 启动窗口-美化按钮5. 登录窗口-美化按钮、雪花视频6. 注册窗口-美化按钮、雪花视频、从启动窗口跳转7. 注册窗口-开启摄像头8. 注…

大恒IFrameData IImageData转bmp HObject Mat

大恒工业相机采集的帧数据转为其他8bit图像格式 C#转为bmp格式转为Halcon的HObject格式转为OpenCVSharp的Mat格式 回调采集图像的数据类型为IFrameData&#xff0c;单帧采集的数据类型为IImageData&#xff0c;两者的区别为IImageData类多了一个**Destroy()**方法 C# 转为bm…

C++标准模板(STL)- 类型支持 (定宽整数类型)(int8_t,int_fast8_t,int_least8_t,intmax_t,intptr_t)

定宽整数类型 类型 定义于头文件 <cstdint> int8_tint16_tint32_tint64_t (可选) 分别为宽度恰为 8、16、32 和 64 位的有符号整数类型 无填充位并对负值使用补码 &#xff08;仅若实现支持该类型才提供&#xff09; (typedef) int_fast8_tint_fast16_tint_fast32_tint…

第二章 线性表

线性表 线性表的基本概念线性表的顺序存储线性表顺序存储的类型定义线性表基本运算在顺序表上的实现顺序表实现算法的分析 线性表的链接存储单链表的类型定义线性表的基本运算在单链表上的实现 其他运算在单链表上的实现建表删除重复结点 其他链表循环链表双向循环链表 顺序实现…

如何将图片存到数据库(以mysql为例), 使用ORM Bee更加简单

如何将图片存到数据库 1. 创建数据库: 2. 生成Javabean public class ImageExam implements Serializable {private static final long serialVersionUID 1596686274309L;private Integer id;private String name; // private Blob image;private InputStream image; //将In…

【算法练习Day12】树的递归遍历非递归遍历

​&#x1f4dd;个人主页&#xff1a;Sherry的成长之路 &#x1f3e0;学习社区&#xff1a;Sherry的成长之路&#xff08;个人社区&#xff09; &#x1f4d6;专栏链接&#xff1a;练题 &#x1f3af;长路漫漫浩浩&#xff0c;万事皆有期待 文章目录 递归遍历前序遍历中序遍历后…

《计算机视觉中的多视图几何》笔记(12)

12 Structure Computation 本章讲述如何在已知基本矩阵 F F F和两幅图像中若干对对应点 x ↔ x ′ x \leftrightarrow x x↔x′的情况下计算三维空间点 X X X的位置。 文章目录 12 Structure Computation12.1 Problem statement12.2 Linear triangulation methods12.3 Geomet…

AndroidStudio精品插件集

官网 项目地址&#xff1a;Github博客地址&#xff1a;Studio 精品插件推荐 使用需知 所有插件在 Android Studio 2022.3.1.18&#xff08;长颈鹿&#xff09;上测试均没有问题&#xff0c;推荐使用此版本Android Studio 2022.3.1.18&#xff08;长颈鹿&#xff09;正式版下…

计算机网络(六):应用层

参考引用 计算机网络微课堂-湖科大教书匠计算机网络&#xff08;第7版&#xff09;-谢希仁 1. 应用层概述 应用层是计算机网络体系结构的最顶层&#xff0c;是设计和建立计算机网络的最终目的&#xff0c;也是计算机网络中发展最快的部分 早期基于文本的应用 (电子邮件、远程登…

【计算机网络】HTTPS协议详解

文章目录 一、HTTPS协议 介绍 1、1 HTTP协议不安全的体现 1、2 什么是 HTTPS协议 二、加密的一些概念 2、1 怎么理解加密 2、2 为什么要加密 2、3 常见的加密方式 2、2、1 对称加密 2、2、2 非对称加密 三、HTTPS协议探究加密过程 3、1 只使用对称加密 3、2 只是用非对称加密 3…