【学校OJ】avl平衡树 普通平衡树

题目描述

你需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作:
1. 插入一个整数x
2. 删除一个整数x(若有多个相同的数,只删除一个)
3. 查询整数x的排名(若有多个相同的数,输出最小的排名),相同的数依次排名,不并列排名
4. 查询排名为x的数,排名的概念同3
5. 求x的前驱(前驱定义为小于x,且最大的数),保证x有前驱
6. 求x的后继(后继定义为大于x,且最小的数),保证x有后继

输入

第一行为n,表示操作的个数(n <= 500000)

下面n行每行有两个数opt和x,opt表示操作的序号(1<=opt<=6,   -10^7 <= x <= 10^7)

大规模输入数据,建立读入优化

输出

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

样例输入

 (如果复制到控制台无换行,可以先粘贴到文本编辑器,再复制)

8
1 10
1 20
1 30
3 20
4 2
2 10
5 25
6 -1

样例输出

2
20
20
20

    好久没诈尸了,不太爽。所以又来一发。。。

    学了avl平衡树,才懂得了什么叫做200行代码。。。然而平衡树写得也是一道猥琐过一道,那么就选取最经典的普通平衡树作为这次诈尸的工具吧!

    题目与题目描述很直接的告诉了我们——平衡树!所以我们可以很容易分析出实现的方法。

    结构体成员配备:左儿子ls,右儿子rs,数值data,高度h,同样数量sum,总结点size(包括左子树、右子树、自己)

    维护方式:ls、rs用旋转维护,data永不变更,h取ls和rs的最大h加1,sum删除插入时维护。size在各种时候都别忘了用ls的size、rs的size和sum相加进行维护

    1.插入节点。这种简单粗暴的事情还需要说吗?直接进行一般的插入,由于使用惰性删除,所以我们可以遇到相同节点就将sum++,就很容易的解决了!当然你也可以用普通删除然后就保存相同节点啦~

    2.删除节点。你们会插入了还不会删除吗?懒人就用惰性删除,你要是真的喜欢普通删除或者希望能过(是的,惰性删除偏偏在学校OJ上过不了TAT,不过大部分OJ都是可以过的),就使用删除后慢慢转到顶部……

    3.查询数的排名。这就比较简单了,因为数是一定存在的,所以只要找到一个节点,然后比较大小即可。惰性删除很好解决排名问题,如果刚好等于,只需输出最小的即可,即avl[avl[x].ls].size+1,看出来了吗?ls全体比自己小,排名只需加一,如果比自己小就往ls找,大的话就加上ls的size和sum,再往rs找即可。直接删除就方便了,处理方法一模一样,只是找到一样的后要先记录一下,再往ls那边找(因为有可能有重复的)。

    4.查询排名的数。就是3的逆操作,依然是比较ls的size就完了,自己慢慢思考吧……还不需要考虑重复的问题

    5.前驱,后继。由于操作过于相似,可以当成一种来看。

    直接删除:如果遇到一个节点,比较大小,然后很直接地转入ls或rs即可,不需要考虑其他的。

    惰性删除:这就比较麻烦了,如果遇到节点,且sum不为0,那么只需要直接转入ls或rs,因为至少有data比另外一边更接近这个数。但是如果sum为0,就不得不两边兼顾一下,不然很有可能某一边虽然不为空,但可能没有一个符合,现在就只能判断一下是否为空,然后慢慢两边搜……这会浪费一些时间……

    至此,我一句代码也没有给你们,然后将这道题讲完了~当然不能坑你们,代码来了(200行+)~

    

#include<cstdio>
#include<algorithm>
#include<climits>
using namespace std;
const int inf=100000000;
int n,cnt,lmax,rmin,root,ans;
inline int getint()
{
	register int f=1,p=0;
	register char c=getchar();
	while((c<'0'||c>'9')&&c!='-')
		c=getchar();
	if(c=='-')
	{
		f=-1;c=getchar();
	}
	for(;c>='0'&&c<='9';)
	{
		p=p*10+c-'0';
		c=getchar();
	}
	p*=f;
	return p;
}
inline void putint(int p)
{
	if(p<0)
	{
		putchar('-');
		p=-p;
	}
	if(p>9)putint(p/10);
	putchar(p%10+'0');
}
struct avl
{
	int ls,rs,p,h,si,sum;
}d[500005];
inline int zig(int r)
{
	register int t=d[r].ls;
	d[r].ls=d[t].rs;
	d[t].rs=r;
	d[r].h=max(d[d[r].rs].h,d[d[r].ls].h)+1;
    d[t].h=max(d[d[t].rs].h,d[d[t].ls].h)+1;
	d[r].si=d[d[r].ls].si+d[d[r].rs].si+d[r].sum;
	d[t].si=d[d[t].ls].si+d[d[t].rs].si+d[r].sum;
	return t;
}
inline int zag(int r)
{
	register int t=d[r].rs;
	d[r].rs=d[t].ls;
	d[t].ls=r;
	d[r].h=max(d[d[r].rs].h,d[d[r].ls].h)+1;
    d[t].h=max(d[d[t].rs].h,d[d[t].ls].h)+1;
	d[r].si=d[d[r].ls].si+d[d[r].rs].si+d[r].sum;
	d[t].si=d[d[t].ls].si+d[d[t].rs].si+d[r].sum;
	return t;
}
inline int zigzag(int r)
{
	d[r].rs=zig(d[r].rs);
	r=zag(r);
	return r;
}
inline int zagzig(int r)
{
	d[r].ls=zag(d[r].ls);
	r=zig(r);
	return r;
}
inline void move(int &q)
{
	if(d[d[q].ls].h-d[d[q].rs].h==2)
	{
		register int t=d[q].ls;
		if(d[d[t].ls].h>d[d[t].rs].h)
			q=zig(q);
		else
			q=zagzig(q);
	}
	else
	{
		if(d[d[q].ls].h-d[d[q].rs].h==-2)
		{
			register int t=d[q].rs;
			if(d[d[t].rs].h>d[d[t].ls].h)
				q=zag(q);
			else
				q=zigzag(q);
		}
	}
	d[q].si=d[d[q].ls].si+d[d[q].rs].si+d[q].sum;
	d[q].h=max(d[d[q].ls].h,d[d[q].rs].h)+1;
}
inline void insert(int a,int &q)
{
	if(q==0)
	{
		q=++cnt;
		d[q].p=a;
		d[q].h=1;
		d[q].si=1;
		d[q].sum=1;
		return;
	}
	if(d[q].p==a)
		d[q].sum++;
	else
	{
		if(d[q].p>a)
			insert(a,d[q].ls);
		else
			insert(a,d[q].rs);
	}
	move(q);
}
inline void dele(int a,int q)
{
	if(q==0)return;
	if(d[q].p==a)
		d[q].sum--;
	else
	{
		if(a<d[q].p)
			dele(a,d[q].ls);
		else
			dele(a,d[q].rs);
	}
	d[q].si=d[d[q].ls].si+d[d[q].rs].si+d[q].sum;
}
inline int find(int k,int q)
{
	if(q==0)return inf;
	register int l=d[q].sum;
	if(k>=d[d[q].ls].si+1&&k<=d[d[q].ls].si+l)return d[q].p;
	else
	{
		if(k<d[d[q].ls].si+1)return find(k,d[q].ls);
		else return find(k-d[d[q].ls].si-l,d[q].rs);
	}
}
inline int ask(int k,int q)
{
	if(q==0)return inf;
	if(d[q].p==k)
		return d[d[q].ls].si+1;
	else
	{
		if(k<d[q].p)
			return ask(k,d[q].ls);
		else
			return ask(k,d[q].rs)+d[d[q].ls].si+d[q].sum;
	}
}
inline void left(int x,int q)
{
	if(q==0)return;
	if(d[q].p<x)
	{
		if(d[q].sum)
			lmax=max(lmax,d[q].p);
		else
			left(x,d[q].ls);
		if(d[d[q].rs].si)
			left(x,d[q].rs);
	}
	else
		left(x,d[q].ls);
}
inline void right(int x,int q)
{
	if(q==0)return;
	if(d[q].p>x)
	{
		if(d[q].sum)
			rmin=min(rmin,d[q].p);
		else
			right(x,d[q].rs);
		if(d[d[q].ls].si)
			right(x,d[q].ls);
	}
	else
		right(x,d[q].rs);
}
int main()
{
	n=getint();
	for(int i=1;i<=n;i++)
	{
		register int opt,p;
		opt=getint();p=getint();
		if(opt==1)
		{
			insert(p,root);
			continue;
		}
		if(opt==2)
		{
			dele(p,root);
			continue;
		}
		if(opt==3)
		{
			ans=ask(p,root);
			putint(ans);
		}
		if(opt==4)
		{
			ans=find(p,root);
			putint(ans);
		}
		if(opt==5)
		{
			lmax=-inf;
			left(p,root);
			putint(lmax);
		}
		if(opt==6)
		{
			rmin=inf;
			right(p,root);
			putint(rmin);
		}
		putchar('\n');
	}
}


    我不会说这段代码是会超时的……

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值