直接看题解,毕竟还没学这种高级数据结构,真是有点难啊!
六种操作(按难度顺序排名)
1.查找(4种:查询x的排名,查询排名为x的数,查找前驱,查找后继)
2.修改(2种:插入,删除)
3.基础维护操作(2种:旋转,伸展)
最简单的:
1.查找类:
只需要根据当前给出的数据结构进行查找就可以了,利用到当前数据结构的特点,但是本身没有对数据结构的理解进行过多的考察,所以最简单
2.修改类:
插入和删除,涉及到对数据结构的理解,并且需要熟悉伸展和旋转操作,算是数据结构的进阶(比较难,需要学会运用一些该数据结构的操作)
3.基础维护操作:
修改类进行的基础,为重难点操作,同时是当前数据结构最具特色的一部分
逐一解决把
数据结构:
1.父亲结点下标
2.双子结点下标
3.当前结点加上当前结点的所有子节点的数量,可以称之为以当前结点作为根节点的树的总结点数
4.当前结点重复次数
5.当前节点的值
1.查找:
参数即为数值x
设置一个u为当前树的根节点
如果是0,那么整个树都是空的,找不到
如果u不是0,那么说明当前树不空
和当前u的val值进行比较
如果大于u的val值
判断当前u的右子树是不是空
如果不空,那么直接更新u为右子树
如果是空的,说明走投无路,找不到
如果小于u的val值
判断当前u的左子树是不是空的
如果不空,那么直接更新u为左子树
如果是空的,说明走投无路,找不到
如果等于
直接break
最终把u伸展到根节点上
2.查询x的排名
直接查找x(目的就是为了把x伸展到根节点上面)
直接输出根节点的左子树的孩子数
3.查询排名为x的数
令u为根节点
判断如果x比当前子树的所有结点总和还要大,直接-1
y为u的左子树
判断当前x的值
如果比左子树的儿子加上u自己的数量都要大
那么说明x的排名在u的后面
直接让x减去左子树儿子数量加上自己的数量,因为在更新u为右子树之后,x的排名数量就是相当于u的右子树来看了
此时更新u的值为右子树
如果小于等于左子树的儿子
说明在左子树中,此时更新u,但是不用更新x
如果二者都不满足,说明x就等于当前的u
直接return u.val即可
4.查询前驱后继:
前驱和后继
1.直接find(x),相当于伸展他的位置到根节点
如果没找到,那么就要判断此时找到的这个最近的结点是是否就是x的前驱后者后继//细节
2.如果是前驱,一开始直接取根节点的左子树
一直访问左子树的右子树,直到当前节点的右子树为空停止
3.如果是后继,那么一开始直接取根节点的右子树
一直访问右子树的左子树,直到当前结点的左子树为空停止
返回值应该是当前查找到符合条件的节点的位置,因为到时候删除操作里面会用到
2.修改
1.删除操作:
1.找到前驱
2.找到后继
3.伸展前驱到根节点
4.伸展后继到前驱的子节点
结果被寻找到结点就到了后继的左子节点
判断当前左子节点的数量是否大于1;
大于1直接减一
等于1则更新后继右子树为0
2.插入操作:
1.设置一u,用来当拨针
2.设置一个ff,用来时刻更新当前u的父节点
3.循环比较要插入的x,
如果u等于0,或者u对应的结点值等于x
直接braek
否则更新u为左子树或者右子树(取决于当前u结点对应的值和x的相对大小)
最终判断u是不是为空
如果不是空,那么相应节点的cnt值++;
如果是空,那么就需要新建结点
tot值++;把这个新建结点填入
左右子节点0;cnt=1;val=x,f=ff,son值等于1
最后把u伸展到子节点上
3.基础性操作
1.cntnum:
函数用来计算给以给定结点为根节点的树的总结点数
左子树的son值加上右子树的son值,最后加上自己的cnt值
2.旋转操作,
需要用if判断好几种不同的情况
取出要旋转结点的父节点y,爷爷结点z
然后判断当前三者的连接情况
说白了,就是先把爷爷结点和孙子节点链起来
如果父亲节点是爷爷节点的左儿子,
那孙子就代替左儿子位置
如果父亲节点是右儿子
那么孙子就代替右儿子的位置
孙子的父亲就变成爷爷
同时判断父亲节点和儿子结点关系
如果儿子是父亲结点的右儿子
那么父亲结点的右儿子变成儿子结点的左儿子
儿子结点的左儿子的父亲变成父亲结点
儿子结点的左儿子变成父亲
如果儿子是父亲结点的左儿子
那么儿子节点的右儿子变成父亲节点的左儿子
儿子节点的右儿子的父亲变成父亲节点
儿子节点的左儿子变成父亲
最后父亲节点的父亲变成儿子
更新儿子节点和父亲节点的总结点值
3.伸展操作:
需要参数:结点编号和伸展到的位置
当我要伸展到的编号的父亲结点不等于我要伸展到的位置时:
那么我就要判断一下伸展情
此时取x的父亲节点和爷爷结点,
当爷爷结点不是我要找到的结点时
判断三者此时的链接情况
三者同线:
那么此时旋转y、
三者不同线:
此时再旋转x
最后再判断的外面,旋转一次x
每次循环都更新父亲节点和爷爷结点
如果goal==0那么直接更新root等于x
结束
写了这么多,说实话我并不是很懂原理
哎。。只能慢慢学了,实在有点费时间啊
好的:
错误1:
旋转出错,逻辑错误,简而言之,转反了
错误2:
插入出错,当x的值在原树中不存在时,需要插入到一个新的空位,同时也要判断其父节点是不是空,如果父节点不是空的,那么我需要更新他的父节点的儿子结点,同时看看是左子树还是右子树
错误3:
最后打印结果,包括函数使用出现了错误
1.前驱后继函数,返回值为下标,而题目要求输出值,所以错误
2.排名函数,由于前面加上一个最大值,所以输入的排名要在x基础上面加一;
#include<stdio.h>
#define maxn 500500
typedef struct
{
int f;
int numN;
int cnt;
int ch[2];
int val;
}TRE;
TRE tree[maxn];
int root=0,tot=0,n,opt,x;
void cntnode(int x)
{
tree[x].numN=tree[tree[x].ch[0]].numN+tree[tree[x].ch[1]].numN+tree[x].cnt;
}
//OK
void rota(int x)
{
int y=tree[x].f;
int z=tree[y].f;
if(tree[z].ch[0]==y)
{
tree[z].ch[0]=x;
}
else
{
tree[z].ch[1]=x;
}
tree[x].f=z;
if(tree[y].ch[1]==x)
{
tree[y].ch[1]=tree[x].ch[0];
tree[tree[x].ch[0]].f=y;
tree[x].ch[0]=y;
}
else
{
tree[y].ch[0]=tree[x].ch[1];
tree[tree[x].ch[1]].f=y;
tree[x].ch[1]=y;
}
tree[y].f=x;
cntnode(y);
cntnode(x);
}
//ok
void splay(int x,int pos)
{
while(tree[x].f!=pos)
{
int y=tree[x].f;
int z=tree[y].f;
if(z!=pos)
{
if(tree[z].ch[0]==y&&tree[y].ch[0]==x)
{
rota(y);
}
else if(tree[z].ch[0]!=y&&tree[y].ch[0]!=x)
{
rota(y);
}
else
{
rota(x);
}
}
rota(x);
}
if(pos==0)
{
root=x;
}
}
void find(int x)
{
int u=root;
if(u==0) return ;
while(1)
{
if(x>tree[u].val)
{
if(tree[u].ch[1]!=0)
{
u=tree[u].ch[1];
}
else
{
break;
}
}
else if(x<tree[u].val)
{
if(tree[u].ch[0]!=0)
{
u=tree[u].ch[0];
}
else
{
break;
}
}
else
{
break;
}
}
splay(u,0);
}
void insert(int x)
{
int u=root;
int ff=0;
while(1)
{
ff=u;
if(x>tree[u].val)
{
u=tree[u].ch[1];
if(u==0)
{
break;
}
}
else if(x<tree[u].val)
{
u=tree[u].ch[0];
if(u==0)
{
break;
}
}
else//相等,说明找到了这个值
{
break;
}
}
if(u==0)//需要添加新东西
{
tot++;
u=tot;
tree[tot].val=x;
tree[tot].ch[0]=0;
tree[tot].ch[1]=0;
tree[tot].f=ff;
if(ff!=0)
{
if(x>tree[ff].val)
{
tree[ff].ch[1]=u;
}
else
{
tree[ff].ch[0]=u;
}
}
tree[tot].cnt=1;
tree[tot].numN=1;
}
else
{
tree[u].cnt++;
}
splay(u,0);//在这里会更新numN的值,不需要在上面加一次了
}
int next(int x,int f)//返回的是找到结点的位置,因为删除操作会用到
{
find(x);
int u=root;
if(tree[u].val>x&&f==1)
{
return u;
}
else if(tree[u].val<x&&f==0)
{
return u;
}
if(f==0)//找前驱
{
u=tree[u].ch[0];
while(tree[u].ch[1]!=0)
{
u=tree[u].ch[1];
}
}
else
{
u=tree[u].ch[1];
while(tree[u].ch[0]!=0)
{
u=tree[u].ch[0];
}
}
return u;
}
void delet(int x)
{
int last=next(x,0);
int later=next(x,1);
splay(last,0);
splay(later,last);
int del=tree[later].ch[0];
if(tree[del].cnt>1)
{
tree[del].cnt--;
splay(del,0);
}
else
{
tree[later].ch[0]=0;
}
}
int check(int x)
{
int u=root;
int y;
if(x>tree[u].numN||x<=0) return -1;
while(1)
{
y=tree[u].ch[0];
if(x>tree[u].cnt+tree[y].numN)
{
x=x-(tree[u].cnt+tree[y].numN);
u=tree[u].ch[1];
}
else if(x<=tree[y].numN)
{
u=y;
}
else
{
return tree[u].val;
}
}
}
int main()
{
insert(-100000000);
insert(+100000000);
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d %d",&opt,&x);
if(opt==1)
{
insert(x);
}
else if(opt==2)
{
delet(x);
}
else if(opt==3)
{
find(x);
printf("%d\n",tree[tree[root].ch[0]].numN);
}
else if(opt==4)
{
printf("%d\n",check(x+1));
}
else if(opt==5)
{
printf("%d\n",tree[next(x,0)].val);
}
else if(opt==6)
{
printf("%d\n",tree[next(x,1)].val);
}
}
return 0;
}