P3369 【模板】普通平衡树(splay 算法)

题目描述

您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作:

  1. 插入一个数 x。
  2. 删除一个数 x(若有多个相同的数,应只删除一个)。
  3. 定义排名为比当前数小的数的个数 +1。查询 x 的排名。
  4. 查询数据结构中排名为 x 的数。
  5. 求 x 的前驱(前驱定义为小于 x,且最大的数)。
  6. 求 x 的后继(后继定义为大于 x,且最小的数)。

对于操作 3,5,6,不保证当前数据结构中存在数 x。

输入格式

第一行为 n,表示操作的个数,下面 n 行每行有两个数 opt 和 x,opt 表示操作的序号(1≤opt≤6)

输出格式

对于操作 3,4,5,6 每行输出一个数,表示对应答案。

输入输出样例

输入 #1复制

10
1 106465
4 1
1 317721
1 460929
1 644985
1 84185
1 89851
6 81968
1 492737
5 493598

输出 #1复制

106465
84185
492737

说明/提示

【数据范围】
对于 100% 的数据,1≤n≤105,∣x∣≤107

解析:

这时一个思维的跨度:

我们自己构造平衡二叉树,左儿子节点小于父节点,右儿子的全部节点大于父节点。

我们需要对二叉树进行左转和右转。

每次进行转动时替代其相对的儿子。

比如:

右旋时:x的右儿子当作y的左儿子。

这里你们可能会问为什么当y的左儿子呢?

答:因为这是一颗平衡二叉树,左儿子一定小于父节点。

而且x为y的左儿子,x的任意一个儿子一定小于其x的父节点。
左旋同理。

实现代码是用异或操作,刚好就是其儿子转动的对立面。比如:x右旋,x的右儿子为y,这时原本的x的右儿子就要当y的左儿子。这样子x的右儿子不会丢失。又满足平衡二叉树的性质。

实现左旋右旋代码如下:

void rotate(int x) // 可以进行左旋 和右旋 
{//先修 z ;在修 y ;在修 x的儿子节点;在修 x本身 int y = tr[x].fa,z = tr[y].fa, k = tr[y].ch[1] == x; // z -> y -> x  我们需要 将 它转为 z->x->y; tr[z].ch[tr[z].ch[1] ==y]  = x, tr[x].fa = z; //  z的 儿子 为 y 替换为 x , 在将 x 的父节点 为 xtr[y].ch[k] = tr[x].ch[k^1];//比如如果 初始 y的左儿子为 x 当替换完。 y的左儿子空了 ,将x的右儿子给y的做儿子 tr[tr[x].ch[k^1]].fa = y; // 从x下的儿子 替换到 y 下时  ,儿子的父节点要变 tr[x].ch[k^1] = y;//这时x那个替换到 y的儿子空位了。tr[y].fa = x;pushup(y);//先push儿子在父节点 pushup(x); 
}

下面就是我们如何维护这个平衡二叉树的树高,尽可能使它的高度最小。

我学到的方法是, 当一棵树他是以直线型,折线型时,如下图所示:

当直线型时,先旋转它的父节点,在旋转它自己本身。

当折线型时,先旋转它自己,在旋它自己。

代码如下:

void splay(int x,int k) //k为父节点 
{while(tr[x].fa != k){int y = tr[x].fa,z = tr[y].fa;//从 x起 2个父节点  if(z != k){ // 如果 是 一条    有折线时 //如果 时一条 直线 时 1^1 为 false ,先选 y 在旋 x  (ls(y) == x)^(ls(z)==y) ? rotate(x):rotate(y);}rotate(x);}if(!k){//等于 0 的时候 才为 根节点 root = x;}
}

还有一个前驱操作:

先用find函数这个节点在那里。

在遍历它的左儿子的右儿子。到达最右的那个儿子。

后继也是一样。

void find(int v)
{int x = root;while(tr[x].ch[v > tr[x].v] && v!=tr[x].v){x = tr[x].ch[v > tr[x].v];}splay(x,0);
}int getpre(int v)
{find(v);int x = root;if(tr[x].v < v){return x;}x = ls(x);while(rs(x)){x = rs(x);}splay(x,0);return x;
}int getsuc(int v)
{find(v);int x = root;if(tr[x].v > v) return x;x = rs(x);while(ls(x)) x = ls(x);splay(x,0);return x; 
}

删除操作:

我们找到它的前驱和后继,再进行把删除的点移到其后继的左儿子上,再进行删除操作。

这个需要画图理解一下。

void del(int v)//删除 
{int pre = getpre(v);int suc = getsuc(v);splay(pre,0);splay(suc,pre);//将它转到左节点方便删除int del = tr[suc].ch[0];if(tr[del].cnt > 1){tr[del].cnt--;splay(del,0);	} else{tr[suc].ch[0] = 0;splay(suc,0);}
}

刚开始插入操作时,我们要进行哨兵的插入。

完整代码如下:

#include<bits/stdc++.h>
using namespace std;#define ls(x) tr[x].ch[0]
#define rs(x) tr[x].ch[1]
const int N = 1100010,INF = (1<<30)-1;
struct node{int ch[2];int fa;int v;int cnt;int siz;void init(int p,int v1){fa = p;v = v1;cnt = siz  = 1;}
}tr[N];
int root = 0,tot = 0;
void pushup(int x)
{tr[x].siz = tr[ls(x)].siz + tr[rs(x)].siz + tr[x].cnt;
}void rotate(int x) // 可以进行左旋 和右旋 
{//先修 z ;在修 y ;在修 x的儿子节点;在修 x本身 int y = tr[x].fa,z = tr[y].fa, k = tr[y].ch[1] == x; // z -> y -> x  我们需要 将 它转为 z->x->y; tr[z].ch[tr[z].ch[1] ==y]  = x, tr[x].fa = z; //  z的 儿子 为 y 替换为 x , 在将 x 的父节点 为 xtr[y].ch[k] = tr[x].ch[k^1];//比如如果 初始 y的左儿子为 x 当替换完。 y的左儿子空了 ,将x的右儿子给y的做儿子 tr[tr[x].ch[k^1]].fa = y; // 从x下的儿子 替换到 y 下时  ,儿子的父节点要变 tr[x].ch[k^1] = y;//这时x那个替换到 y的儿子空位了。tr[y].fa = x;pushup(y);//先push儿子在父节点 pushup(x); 
}void splay(int x,int k) //k为父节点 
{while(tr[x].fa != k){int y = tr[x].fa,z = tr[y].fa;//从 x起 2个父节点  if(z != k){ // 如果 是 一条    有折线时 //如果 时一条 直线 时 1^1 为 false ,先选 y 在旋 x  (ls(y) == x)^(ls(z)==y) ? rotate(x):rotate(y);}rotate(x);}if(!k){//等于 0 的时候 才为 根节点 root = x;}
}void insert(int v) //插入 节点  
{int x = root,p = 0;while(x&& tr[x].v != v){ // 弹出 条件 x 为 0节点 找不到  或者 找到当前节点  p = x, x = tr[x].ch[v > tr[x].v];}if(x) {tr[x].cnt++;}else{ // 新创建 一个节点 x = ++tot;tr[p].ch[v>tr[p].v] = x;tr[x].init(p,v); }splay(x,0);
}void find(int v)
{int x = root;while(tr[x].ch[v > tr[x].v] && v!=tr[x].v){x = tr[x].ch[v > tr[x].v];}splay(x,0);
}int getpre(int v)
{find(v);int x = root;if(tr[x].v < v){return x;}x = ls(x);while(rs(x)){x = rs(x);}splay(x,0);return x;
}int getsuc(int v)
{find(v);int x = root;if(tr[x].v > v) return x;x = rs(x);while(ls(x)) x = ls(x);splay(x,0);return x; 
}void del(int v)//删除 
{int pre = getpre(v);int suc = getsuc(v);splay(pre,0);splay(suc,pre);//将它转到左节点方便删除int del = tr[suc].ch[0];if(tr[del].cnt > 1){tr[del].cnt--;splay(del,0);	} else{tr[suc].ch[0] = 0;splay(suc,0);}
}int getrank(int v)
{insert(v);int res = tr[tr[root].ch[0]].siz;del(v);return res;
}int getval(int k) //第k个值 
{int x = root;while(true){if(k <= tr[ls(x)].siz) x = ls(x);else if(k <= tr[ls(x)].siz + tr[x].cnt) break;else k -= tr[ls(x)].siz + tr[x].cnt,x = rs(x);}splay(x,0);return tr[x].v;
}int main()
{insert(-INF);//加入哨兵 insert(INF);int n,op,x;scanf("%d",&n);while(n--){scanf("%d%d",&op,&x);if(op == 1){insert(x); }else if(op == 2){del(x);}else if(op == 3){printf("%d\n",getrank(x));}else if(op == 4){printf("%d\n",getval(x+1)); //因为有哨兵 }else if(op == 5){printf("%d\n",tr[getpre(x)].v);}else{printf("%d\n",tr[getsuc(x)].v);}}return 0;	
}  

时间复杂度为:O(n*logn)

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【MySQL】DML的表操作详解:添加数据&修改数据&删除数据(可cv例题语句)

前言 大家好吖&#xff0c;欢迎来到 YY 滴MySQL系列 &#xff0c;热烈欢迎&#xff01; 本章主要内容面向接触过C Linux的老铁 主要内容含&#xff1a; 欢迎订阅 YY滴C专栏&#xff01;更多干货持续更新&#xff01;以下是传送门&#xff01; YY的《C》专栏YY的《C11》专栏YY的…

Fastgpt 无法启动或启动后无法正常使用的讨论(启动失败、用户未注册等问题这里)

FastGPT 是一个基于 LLM 大语言模型的知识库问答系统&#xff0c;提供开箱即用的数据处理、模型调用等能力。同时可以通过 Flow 可视化进行工作流编排&#xff0c;从而实现复杂的问答场景&#xff01; FastGPT是非常实用并且相当厉害的个人知识库AI项目&#xff0c;项目是非常…

python练习四

1. 求一个十进制的数值的二进制的0、1的个数 def count_binary_ones(n):binary_str bin(n)[2:] # 转换为二进制字符串&#xff0c;去除前缀0bprint(f"{n} 的二进制为: {binary_str}")return binary_str.count(0), binary_str.count(1) n int(input("输入一个…

20240329-科技咨询:比亚迪第五代DMi;央视AI《周处除三害》;带屏幕苹果耳机爆火

一、比亚迪5月份即将推出第五代DMi技术 近日&#xff0c;比亚迪举行了2023年财报投资人沟通会。会议纪要显示&#xff0c;比亚迪董事长王传福在会上透露&#xff0c;今年5月将推出第五代DMI混动技术&#xff0c;预计馈电油耗将降至2.9升/百公里&#xff0c;而满油满电续航将达…

自建机房私有云吗?

大家好&#xff0c;我是小码哥&#xff0c;之前一种有没搞清楚公有云、私有云的概念&#xff0c;今天算是弄清楚了&#xff0c;这里给大家分享一下公有云、私有云的区别&#xff0c;以及自建机房算不算私有云&#xff01; 其实私有云&#xff08;Private Cloud&#xff09;和公…

android安卓英语学习课设

一、关于这个项目ELAPP 该项目是一个基于java开发的服务器-客户端模式的安卓英语学习软件&#xff0c;主要功能点就是背单词&#xff0c;中英文翻译&#xff0c;OCR文字翻译。 服务器端使用springboot&#xff0c;mybatisplus&#xff0c;MySQL&#xff0c;mongodb&#xff0…

Ubuntu20.04下PCL安装,查看,卸载等操作

Ubuntu20.04下PCL安装&#xff0c;查看&#xff0c;卸载等操作 项目来源 https://github.com/PointCloudLibrary/pclhttps://pointclouds.org/documentation/modules.htmlhttps://pcl.readthedocs.io/projects/tutorials/en/master/ 点云学习&#xff1a; https://github.c…

【JavaWeb】Day27.Web入门——Tomcat介绍

目录 WEB服务器-Tomcat 一.服务器概述 二.Web服务器 三.Tomcat- 基本使用 1.下载 2.安装与卸载 3.启动与关闭 4.常见问题 四.Tomcat- 入门程序 WEB服务器-Tomcat 一.服务器概述 服务器硬件&#xff1a;指的也是计算机&#xff0c;只不过服务器要比我们日常使用的计算…

穿透业务报表和回报方案,看见广汽集团的长期价值

在大多数内卷的领域&#xff0c;企业总是有各种各样的理由延缓或者停止回馈资本市场。所以&#xff0c;当我们在竞争激烈的汽车市场&#xff0c;发现这样一家始终将投资者回报放在重要位置的老牌车企时&#xff0c;会觉得有些惊讶。因为伴随着经营和分红上的高确定性&#xff0…

【网络安全技术】——密码技术基础与身份认证技术(学习笔记)

&#x1f4d6; 前言&#xff1a;加密技术是目前网络安全的基础。数据加密技术是指对在网络中所发送的明文消息用加密密钥加密成密文进行传送&#xff0c;接收方用解密密钥进行解密再现明文消息&#xff0c;从而保证传输过程中密文信息即使被泄漏&#xff0c;在无密钥的情况下仍…

工业项目中你连IIoT系统都没见过?

什么是IIoT 工业物联网&#xff08;IIoT&#xff09;是信息技术和制造业融合的产物&#xff0c;利用物联网技术连接工业设备、传感器、系统和人员&#xff0c;实现工业生产数字化、智能化、自动化。IIoT提升生产效率、品质、减低成本和资源利用&#xff0c;打造智能化工业。 功…

pip永久修改镜像地址

修改命令&#xff1a; pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple/ 效果&#xff1a; 会在C:\Users\PC(用户名)\AppData\Roaming\pip目录下新增或修改文件pip.ini 文件内容&#xff1a; [global] index-url https://pypi.tuna.tsinghua.e…