• 看八股408数据结构中平衡树有感而发,直接手撸了10h Splay终于撸出来了


    首先八股中的平衡树是AVL,他在手撸的时候实在是太难写了,因为要找到最小不平衡子树,然后判断四种情况也就是LL,LR,RR,RL,旋转后还得维护各种信息。

    但是为了能够现在手撸平衡树,我直接找到了Splay,这个看起来好撸一点,因为旋转无非就两个了,也就是左旋或右旋了,顶多就是Splay的时候再判断是否爷孙三个一条线罢了。

    但是毕竟第一次用指针写这样比较复杂的数据结构,还是撸了好久好久,得两天花了10h吧,最终洛谷T了最后一个点,Acwing过了,500多ms,用洛谷的treap似乎是400多ms,差不多,毕竟这个指针他就是慢啊,但是除了判断NULL之外其他的都比数组好写啊。

    对于Splay实际上真的是很容易的平衡树了,首先要弄明白的就是两种旋转右旋左旋,这个跟AVL一模一样的,然后就是独特的Splay操作,也就是把一个节点旋转到根节点,这个复杂度大约是(logN)的因为保持平衡树状态旋转的嘛,然后根据一些别人总结出来的什么规律,这样也不慢,也很快。

    对于平衡树来说,无非就是插入删除了。

    Splay操作

    (1)旋转: 旋转分为左旋或者右旋,跟AVL的是一样的。不过只需要知道他是左孩子还是右孩子直接转就好了,这可比四种AVL旋转算什么平衡因子要好写多了。
    (2)Splay: 实际上就是把一个节点通过旋转给他旋转到根上,这里需要注意一下就是要判断是否是爷孙在一条线上,也就是是否三点一线,如果是就旋转爹,否则就旋转自己。

    插入

    Splay插入跟AVL一模一样,但是不要忘记最后把插入的节点Splay到根节点上去

    删除

    Splay删除就比较麻烦了,首先是找到要删除的节点,把他Splay到根上去,然后进行判断。
    (1) 如果当前节点还有剩余的个数,也就是有重复值,重复值--,不变。
    (2) 如果说这个节点的左孩子或者右孩子是NULL的话,那么直接删除即可,唯一的孩子继承成为根。
    (3) 如果左右孩子都存在的话,那么先把根删除,现在就是两颗Splay树了,接着再把左子树的最右节点Splay到左子树根上,这样这个根节点的右孩子一定是NULL然后用它去连接右子树。

    查询一个数的排名

    BST操作,往右就加上左子树的size,最后+1

    查询rank为k的数

    BST操作,拿着去跟左子树的size以及节点的cnt比较,然后走着判断就行

    查询前驱

    因为这个前驱可能不在树里面,先把这个数插入到Splay中,然后把这个节点Splay到根节点,接着去找左子树最右节点最后对这个根节点进行一次del操作

    查询后继

    因为这个前驱可能不在树里面,先把这个数插入到Splay中,然后把这个节点Splay到根节点,接着去找右子树最左节点最后对这个根节点进行一次del操作

    最终代码(手撸的第一个版本,比较粗糙,之后会重新整合一下)

    #include <bits/stdc++.h>
    using namespace std;
    struct Splay
    {
    	Splay *l=NULL,*r=NULL,*parent=NULL;
    	int sz=0,cnt=0,num=0;
    	Splay(int n)
    	{
    		num=n;
    		cnt=1;
    	}
    }*root=NULL;
    void up(Splay *p)
    {
    	p->sz=p->cnt;
    	if(p->l!=NULL) p->sz+=p->l->sz;
    	if(p->r!=NULL) p->sz+=p->r->sz;
    }
    int get(Splay *p)
    {
    	return p->parent==NULL?-1:p->parent->r==p;
    }
    void zig(Splay *p)
    {
    	int now=get(p->parent);
    	p->parent->l=p->r;
    	if(p->r!=NULL) p->r->parent=p->parent;
    	p->r=p->parent;
    	if(p->parent->parent!=NULL) 
    	{
    		p->parent=p->parent->parent;
    		if(!now) p->parent->l=p;
    		else p->parent->r=p;
    	}
    	else p->parent=NULL;
    	p->r->parent=p;
    	up(p->r);up(p);
    }
    void zag(Splay *p)
    {
    	int now=get(p->parent);
    	p->parent->r=p->l;
    	if(p->l!=NULL) p->l->parent=p->parent;
    	p->l=p->parent;
    	if(p->parent->parent!=NULL) 
    	{
    		p->parent=p->parent->parent;
    		if(!now) p->parent->l=p;
    		else p->parent->r=p;
    	}
    	else p->parent=NULL;
    	p->l->parent=p;
    	up(p->l);up(p); 
    }
    void rotate(Splay *p)
    {
    	if(!get(p)) zig(p);
    	else zag(p);
    }
    Splay *splay(Splay* p)
    {
    	while(p->parent!=NULL) rotate(get(p)==get(p->parent)?p->parent:p);
    	return p;
    }
    Splay *ins(int num)
    {
    	Splay *parent=NULL;
    	Splay *p=root;
    	while(p!=NULL)
    	{
    		parent=p;
    		if(p->num==num) 
    		{
    			p->cnt++;
    			up(p);
    			if(p->parent!=NULL) up(p->parent);
    			return splay(p);
    		}
    		else if(p->num>num) p=p->l;
    		else p=p->r;	
    	}
    	p=new Splay(num);
    	p->parent=parent;
    	if(p->parent!=NULL)
    	{
    		if(p->num>p->parent->num) p->parent->r=p;
    		else p->parent->l=p;
    	}
    	up(p);
    	if(p->parent!=NULL) up(p->parent);
    	return splay(p);	
    }
    int find(int x)
    {
    	Splay *p=root;
    	int ans=0;
    	while(p!=NULL)
    	{
    		if(p->num==x) 
    		{
    			if(p->l!=NULL)ans+=p->l->sz;
    			break;
    		}
    		else if(p->num>x) p=p->l;
    		else ans+=(p->l==NULL?0:p->l->sz)+p->cnt,p=p->r;
    	}
    	return ans+1;
    }
    Splay *del(int x)
    {
    	Splay *p=root;
    	int f=0;
    	while(1)
    	{
    		if(p->num==x) p=splay(p),f=1;
    		else if(p->num>x) p=p->l;
    		else p=p->r;
    		if(f) break;
    	}
    	if(p->cnt>1) 
    	{
    		p->cnt--;up(p);return p;
    	}
    	Splay *now=p;
    	if(p->l==NULL) 
    	{
    		if(p->r==NULL) return NULL;
    		else p=p->r,p->parent=NULL,delete(now);return p;	
    	} 
    	if(p->r==NULL)
    	{
    		if(p->l==NULL) return NULL;
    		else p=p->l,p->parent=NULL,delete(now);return p;	
    	}
    	Splay *r=p->r;
    	p=p->l;delete(now);
    	while(p->r!=NULL) p=p->r;
    	p=splay(p);
    	p->r=r;
    	p->r->parent=p;
    	up(p);
    	return p;
    }
    int srank(int x)
    {
    	Splay *p=root;
    	while(1)
    	{
    		int ls=0; 
    		if(p->l!=NULL) ls+=p->l->sz;
    		if(x>ls+p->cnt) x-=ls+p->cnt,p=p->r;
    		else if(x>ls&&x<=p->cnt+ls) return p->num;
    		else p=p->l;
    	} 
    }
    int pre(int x)
    {
    	root=ins(x);
    	Splay *p=root->l;
    	while(p->r!=NULL) p=p->r;
    	root=del(x);
    	return p->num;
    }
    int after(int x)
    {
    	root=ins(x);
    	Splay *p=root->r;
    	while(p->l!=NULL) p=p->l;
    	root=del(x);
    	return p->num;
    }
    void solve()
    {
    	int op,x;
    	scanf("%d%d",&op,&x);
    	if(op==1) root=ins(x);
    	if(op==2) root=del(x);
    	if(op==3) printf("%d
    ",find(x));
    	if(op==4) printf("%d
    ",srank(x));
    	if(op==5) printf("%d
    ",pre(x));
    	if(op==6) printf("%d
    ",after(x));
    }
    int main()
    {
    	int T;
    	scanf("%d",&T);
    	while(T--) solve();
    }
    
  • 相关阅读:
    [001]
    SpringBoot默认首页跳转设置
    Tomcat网站根目录设置
    SpringBoot获取前端传递JSON的几种方法
    MySQL调优性能监控之show profile
    MySQL新特性MTS
    Java线程池拒绝策略
    快速排序与荷兰国旗及Partition问题
    数据结构与算法之返回最小和问题
    MySQL之谓词下推
  • 原文地址:https://www.cnblogs.com/baccano-acmer/p/15015079.html
Copyright © 2020-2023  润新知