普通平衡树

news2024/11/18 19:47:30

题意:略,题中较清晰。

用二叉查找树来存储数据,为了增加效率,尽量使左子树和右子树的深度差不超过一,这样可以时间控制在logn,效率比较高。

右旋和左旋,目的是为了维护二叉树的操作,使其尽量平衡。

int n, m;
int o[N];
struct Node { // 节点
	int l, r; // 左儿子,右儿子
	int key, val; // 数据值,随机值(用以维护二叉树尽量平衡的条件) 
	int cnt, size; // 当前key值的数量,当前子树的所有节点的cnt值和
} tr[N];
int root, idx; // 根节点,下一个可以分配的节点序号

void push_up(int p) { // 与线段树操作的意义一样
	tr[p].size = tr[tr[p].l].size + tr[tr[p].r].size + tr[p].cnt; // 左右子树的size和加上本身cnt
}


int get_node(int key) { // 创建一个节点,返回节点序号
	tr[++ idx].key = key; // 初始化key
	tr[idx].val = rand(); // 随机一个01给val
	tr[idx].cnt = tr[idx].size = 1; // 数量为1,只有本身
	return idx; // 返回序号
}

void build() { // 建立一个空的二叉树,只有两个哨兵,无穷大与无穷小
	get_node(-INF), get_node(INF);
	root = 1, tr[1].r = 2; 
	push_up(root);
}

void zig(int &p) { // 右旋
	int q = tr[p].l; // q为根节点左儿子
	tr[p].l = tr[q].r, tr[q].r = p, p = q; // 对应图片分析
	push_up(tr[p].r), push_up(p); // 更新size值
}

void zag(int &p) { // 左旋
	int q = tr[p].r; // q为根节点右儿子
	tr[p].r = tr[q].l, tr[q].l = p, p = q; // 对应图片分析
	push_up(tr[p].l), push_up(p); // 更新size值
}

void insert(int &p, int key) { // 插入一个节点key
	if(!p) p = get_node(key); // 该key值未出现过,创建一个新的节点,并将序号返回到上一级
	else if(tr[p].key == key) tr[p].cnt ++; // 出现过,直接cnt数量加一
	else if(tr[p].key > key) { // 应该插在左儿子
		insert(tr[p].l, key); // 递归左儿子
		if(tr[tr[p].l].val > tr[p].val) zig(p); // 左儿子val值大于本身,右旋处理
	} else { // 应该插在右儿子
		insert(tr[p].r, key); // 递归右儿子
		if(tr[tr[p].r].val > tr[p].val) zag(p); // 右儿子var值大于本身,左旋处理
	}
	push_up(p); // 更新size状态
	return ;
}

void remove(int &p, int key) { // 删除一个key值节点
	if(!p) return ; // 没找到,直接结束
	if(tr[p].key == key) { // 找到key值节点
		if(tr[p].cnt > 1) tr[p].cnt --; // 数量不唯一,直接减一即可
		else if(tr[p].l || tr[p].r) { // 数量唯一且存在儿子
			if(!tr[p].r || tr[tr[p].l].val > tr[tr[p].r].val) {// 右儿子存在或者左儿子var值大于右儿子,右旋处理
				zig(p);
				remove(tr[p].r, key);// 右旋之后key值节点交换到当前p节点的右儿子,遍历右儿子,一直递归直到没有儿子的时候删除
			} else {// 应该进行左旋处理
				zag(p);
				remove(tr[p].l, key);// 左旋之后key值节点交换到当前p节点的左儿子,遍历左儿子,一直递归直到没有儿子的时候删除
			}
		} else p = 0; // 数量唯一且没有儿子,直接删除即可。
	} else if(tr[p].key > key) remove(tr[p].l, key); // key值点在左儿子
	else remove(tr[p].r, key); // key值点在右儿子
	push_up(p);
}

int get_rank_by_key(int p, int key) { // 根据key值找排名
	if(!p) return 1;  // 没找到直接return 1,因为洛谷这个题说的是不存在的数的排名为比它的数量加一
	if(tr[p].key == key) return tr[tr[p].l].size + 1; // 找到key值,返回key值在当前子树的排名
	if(tr[p].key > key) return get_rank_by_key(tr[p].l, key);// key在左子树
	return get_rank_by_key(tr[p].r, key) + tr[tr[p].l].size + tr[p].cnt; // key在右子树,因为左子树以及本身都是比它小的,所以需要加上这些数量,再去递归右子树,计算key值在右子树的排名
}
int get_key_by_rank(int p, int rank) {  // 找到排名为rank的key值
	if(!p) return INF; // 没找到,不存在这个排名的数
	if(tr[tr[p].l].size >= rank) return get_key_by_rank(tr[p].l, rank); // 在左子树
	if(tr[tr[p].l].size + tr[p].cnt >= rank) return tr[p].key; // 在本身
	return get_key_by_rank(tr[p].r, rank - tr[tr[p].l].size - tr[p].cnt); // 在右子树,需要减去左子树以及本身的数量	
}
int get_prev(int p, int key) { // 获得比key小的最大数
	if(!p) return -INF; // 没找到
	if(tr[p].key >= key) return get_prev(tr[p].l, key); // 在左子树
	return max(tr[p].key, get_prev(tr[p].r, key)); // 本身和右子树都比key小,都有可能,递归右子树与本身进行判断。
}
int get_next(int p, int key) { // 获得比key大的最小数
	if(!p) return INF; // 没找到
	if(tr[p].key <= key) return get_next(tr[p].r, key); // 在右子树
	return min(tr[p].key, get_next(tr[p].l, key));  // 本身和左子树都比key大,都有可能,递归左子树与本身进行判断。
}
inline void sovle() {
	build();
//	cout << idx << endl;
	cin >> n;
	while(n --) {
		int opt, x;
		cin >> opt >> x;
		if(opt == 1) insert(root, x);
		else if(opt == 2) remove(root, x);
		else if(opt == 3) cout << get_rank_by_key(root, x) - 1 << endl;
		else if(opt == 4) cout << get_key_by_rank(root, x + 1) << endl;
		else if(opt == 5) cout << get_prev(root, x) << endl;
		else cout << get_next(root, x) << endl;
	}
}

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

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

相关文章

NX二次开发UF_CSYS_ask_matrix_of_object 函数介绍

文章作者&#xff1a;里海 来源网站&#xff1a;https://blog.csdn.net/WangPaiFeiXingYuan UF_CSYS_ask_matrix_of_object Defined in: uf_csys.h int UF_CSYS_ask_matrix_of_object(tag_t object_id, tag_t * matrix_id ) overview 概述 Gets the matrix identifier atta…

Sealos 云操作系统私有化部署教程

Sealos 私有云已经正式发布了&#xff0c;它为企业用云提供了一种革命性的新方案。Sealos 的核心优势在于&#xff0c;它允许企业在自己的机房中一键构建一个功能与 Sealos 公有云完全相同的私有云。这意味着企业可以在自己的控制和安全范围内&#xff0c;享受到公有云所提供的…

Web前端—移动Web第五天(媒体查询、Bootstrap、综合案例-alloyTeam)

版本说明 当前版本号[20231122]。 版本修改说明20231122初版 目录 文章目录 版本说明目录移动 Web 第五天01-媒体查询基本写法书写顺序案例-左侧隐藏媒体查询-完整写法关键词 / 逻辑操作符媒体类型媒体特性 媒体查询-外部CSS 02-Bootstrap简介使用步骤下载使用 栅格系统全局…

数据库|TiDB v7.1.0 资源管控功能是如何降低运维难度和成本

目录 一、前言 二、资源管控流程图 三、资源管控(Resource Control)测试 &#xff08;一&#xff09;测试集群环境 &#xff08;二&#xff09;Request Unit(RU)概念 &#xff08;三&#xff09;资源管控参数 &#xff08;四&#xff09;评估实际负载所需容量 &#xff…

vue项目引入element-plus

文章目录 引入框架遇到的问题引入的时候&#xff0c;报错 ...(reading replace)...报错&#xff1a;The template root requires ...eslint报错&#xff1a; 运行 引入框架 使用add引入 遇到的问题 引入的时候&#xff0c;报错 …(reading ‘replace’)… Cannot read prop…

Navicat 技术指引 | 适用于 GaussDB 的数据迁移工具

Navicat Premium&#xff08;16.2.8 Windows版或以上&#xff09; 已支持对 GaussDB 主备版的管理和开发功能。它不仅具备轻松、便捷的可视化数据查看和编辑功能&#xff0c;还提供强大的高阶功能&#xff08;如模型、结构同步、协同合作、数据迁移等&#xff09;&#xff0c;这…

git本地账户如何从一台电脑迁移到另外一台

为了表述方便&#xff0c;我们此处用旧电脑、新电脑指代。 在新电脑上安装git 例如&#xff0c;我旧电脑上安装的git版本是2.33.1版本&#xff0c;新电脑安装git的版本是2.43.0&#xff0c;这不妨碍迁移。 将git的全局配置文件从旧电脑拷贝到新电脑 Git的全局配置文件&…

el-table表格排序(需要后端判别),el-table导出功能(向后端发送请求)

&#xff08;1&#xff09;表格排序 &#xff08;2&#xff09;简单的table导出功能&#xff08;需要后台支撑&#xff09;必须要有iframe &#xff08;3&#xff09;页面所有代码&#xff1a; <template><div class"mainContainer"><el-form:model&…

Selenium安装WebDriver最新Chrome驱动(114以后的版本)

&#x1f4e2;专注于分享软件测试干货内容&#xff0c;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指正&#xff01;&#x1f4e2;交流讨论&#xff1a;欢迎加入我们一起学习&#xff01;&#x1f4e2;资源分享&#xff1a;耗时200小时精选的「软件测试」资…

都被“锟斤拷”毒害过,那么究竟是为什么会出现这些奇怪的字符?

不管是在工作中还是生活中&#xff0c;都被“锟斤拷”毒害过&#xff0c;比如这样&#xff1a; 或者这样&#xff1a; 还有这样&#xff1a; 那么究竟是为什么会出现这些奇怪的字符&#xff1f; ASCII编码 在计算机底层都是用0和1进行存储的&#xff0c;ASCII编码将所有的字母…

oracle数据库巡检常见脚本-系列二

简介 作为数据库管理员&#xff08;DBA&#xff09;&#xff0c;定期进行数据库的日常巡检是非常重要的。以下是一些原因&#xff1a; 保证系统的稳定性&#xff1a;通过定期巡检&#xff0c;DBA可以发现并及时解决可能导致系统不稳定的问题&#xff0c;如性能瓶颈、资源利用率…

Navicat 技术指引 | 适用于 GaussDB 的备份与还原功能

Navicat Premium&#xff08;16.2.8 Windows版或以上&#xff09; 已支持对 GaussDB 主备版的管理和开发功能。它不仅具备轻松、便捷的可视化数据查看和编辑功能&#xff0c;还提供强大的高阶功能&#xff08;如模型、结构同步、协同合作、数据迁移等&#xff09;&#xff0c;这…

Linux反弹SHell与检测思路

免责声明 文章仅做经验分享用途,利用本文章所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,作者不为此承担任何责任,一旦造成后果请自行承担!!! 反弹shell payload在线生成 https://www.chinabaiker.com/Hack-Tools/ Online - Reverse Shell G…

C++基础从0到1入门编程(四)类和对象

系统学习C 方便自己日后复习&#xff0c;错误的地方希望积极指正 往期文章&#xff1a; C基础从0到1入门编程&#xff08;一&#xff09; C基础从0到1入门编程&#xff08;二&#xff09; C基础从0到1入门编程&#xff08;三&#xff09; 参考视频&#xff1a; 1.黑马程序员匠心…

docker启动容器失败,然后查看日志,docker logs查看容器出现报错:

docker 启动容器失败&#xff0c;然后docker logs 查看容器出现报错&#xff1a; error from daemon in stream: Error grabbing logs: invalid character l after object key:value pair在网上看到的 解决方案&#xff1a; 找到你日志文件目录&#xff1a; docker inspect …

信用卡不在身上怎么查安全码

信用卡安全码是由3位数字组成的&#xff0c;一般位于信用卡背面签名栏旁边。如果信用卡不在身上&#xff0c;可以通过拨打发卡银行客服热线来查询安全码。但是&#xff0c;安全码是非常私密的信息&#xff0c;客服可能没有权限查询。因此&#xff0c;这个方法不一定有用。另外&…

Ocam——自由录屏工具~

当我们想要做一些混剪、恶搞类型的视频时&#xff0c;往往需要源影视作品中的诸多素材&#xff0c;虽然可以通过裁减mp4文件的方式来获取片段&#xff0c;但在高画质的条件下&#xff0c;mp4文件本身通常会非常大&#xff0c;长此以往&#xff0c;会给剪辑工作带来诸多不便&…

芯片的测试方法

半导体的生产流程包括晶圆制造和封装测试&#xff0c;在这两个环节中分别需要完成晶圆检测(CP, Circuit Probing)和成品测试(FT, Final Test)。无论哪个环节&#xff0c;要测试芯片的各项功能指标均须完成两个步骤&#xff1a;一是将芯片的引脚与测试机的功能模块连接起来&…

CCC联盟——UWB MAC(一)

本文在前面已经介绍了相关UWB的PHY之后&#xff0c;重点介绍数字钥匙&#xff08;Digital Key&#xff09;中关于MAC层的相关实现规范。由于MAC层相应涉及内容比较多&#xff0c;本文首先从介绍UWB MAC的整体框架&#xff0c;后续陆续介绍相关的网络、协议等内容。 1、UWB MAC架…

好用的团队协同办公软件推荐!企业办公必备!

有什么好用的团队协同办公软件可以推荐&#xff1f; 想要的办公软件需要满足“即时通讯”、“多端适配”、“项目管理”、“文件传输”这4大能力。 下面就给大家分享3大类能够满足题主需求的企业级办公软件&#xff0c;免费的付费的都有&#xff0c;也都是侧重的不同领域&…