麻豆小视频在线观看_中文黄色一级片_久久久成人精品_成片免费观看视频大全_午夜精品久久久久久久99热浪潮_成人一区二区三区四区

首頁 > 學院 > 開發設計 > 正文

關于平衡樹(Splay)的一些總結

2019-11-14 12:38:14
字體:
來源:轉載
供稿:網友

        所謂Splay Tree一是這樣一種二叉樹:對于任意一個節點,節點所維護的值大于它的左子樹的所有值,而小于它的右子樹的所有值,這樣的一棵樹平衡樹叫Splay Tree。

        和所有的平衡樹一樣,Splay的最基本操作是rotate。要維持一棵樹的平衡,使得每次操作的復雜度均攤能有logn級別,需要我們不斷的調整樹的形態,rotate操作就能實現這樣的功能。若某一節點x為y的左兒子,那么rotate之后他們的父子關系改變,但仍然滿足Splay的性質,具體如圖:

                                                                     

        可以看出左旋和右旋還是有一定區別的,但是最后都能達到目的。x以及x的子樹都比y小,故y的左兒子變為x的右兒子,y變成x的右兒子……具體代碼如下(對于rotate操作有些人把左旋右旋利用位運算和bool合并了,但是這樣看起來代碼簡潔,但是常數比較大,于是我便舍棄了這種寫法):

inline void rotate(int x){	int fa=tree[x].fa,grand=tree[fa].fa;				//grand為爺爺,在旋轉時爺爺若存在也要做相應變化	if (get(x)) 							//get(x)判斷x是左兒子還是右兒子,x是右兒子則左旋	{		tree[fa].r=tree[x].l;		tree[tree[fa].r].fa=fa;		tree[x].l=fa;	} else							  	//左兒子右旋	{		tree[fa].l=tree[x].r;		tree[tree[fa].l].fa=fa;		tree[x].r=fa;	}	tree[x].fa=grand;						//修改父子關系	tree[fa].fa=x;	if (grand) 							//判斷并修改爺爺	{		if (tree[grand].r==fa) tree[grand].r=x;		else if (tree[grand].l==fa) tree[grand].l=x;		//注意一定是else if		}}

        完成了rotate,接下來就是splay了,所謂splay操作其實就是把某一個點通過不斷的旋轉,把它變為根結點,這樣的好處就是方便對該點進行操作,這個作用在以后會凸顯出來,沒什么好說的,操作簡單直接上代碼:

inline void splay(int x){	for(;tree[x].fa;rotate(x));					//很有特色的寫法,好好琢磨	root=x;								//注意變更整棵樹的root}

這里有一個小小的疑問,看了其他大牛的blog,有些說兒子父親爺爺三點一線的情況要特殊判斷,但是博主試了幾道題沒有特判也對了,于是便省去了特判。若有大牛能解釋為什么需要特判我將不勝感激。

        然后就是基本的insert操作了,要insert首先得找對該節點的存放位置,根據大小關系,若num小于當前點則肯定在左子樹,若等于則就是該點,若大于則是在右子樹,代碼:

inline void insert(int x){	if (root==0)							//若當前為空樹則直接加	{		clear(1);						//clear,清除某點		tree[++sz].num=x;		tree[sz].size=tree[sz].cnt=1;				//size為該子樹的節點數目,cnt為同一數值數字數目		root=sz; return;					//sz為整棵樹的節點數目	} 	int i=root,fa=0;	while (1)	{		if (tree[i].num==x)					//相等,找到位置		{			tree[i].cnt++;			splay(i);					//目前并沒有理解為什么這里要splay			return;		}		fa=i;		i=(x>tree[i].num?tree[i].r:tree[i].l);			//按大小找		if (i==0)						//該點為被添加過,新建節點		{			sz++; 			clear(sz);			tree[sz].num=x;			tree[sz].cnt=tree[sz].size=1;			tree[sz].fa=fa;			(x>tree[fa].num?tree[fa].r:tree[fa].l)=sz;	//設置父親								splay(sz);					//也不理解為什么要splay,但是不加會錯			return;		}	}}

        find操作則是找所有數中第x小(大)的數字,也是一樣利用二分在樹上一半一半的找。

inline int find(int x)                   				//找數字x在所有數字中的排位,返回排位 {	int ans=0,i=root;	while (1)		if (x>=tree[i].num)		{			ans+=(tree[i].l?tree[tree[i].l].size:0);	//若無左兒子則加0			if (x==tree[i].num) 			{				splay(i);				return ans+1;			}			ans+=tree[i].cnt;			i=tree[i].r;		} else i=tree[i].l;}

      del操作,刪除第x小(大)的點,與insert類似,先找到位置令locate=find(x),然后clear,并維護左右兒子的性質。

inline void del(int x){	int locate=find(x);			if (tree[root].cnt>1) 	{		tree[root].cnt--;		return;	}	if ((!tree[root].l)&&(!tree[root].r))	{		clear(root);		root=0;		return;	}	if (!tree[root].l)	{		int old=root;		root=tree[old].r;		tree[root].fa=0;		clear(old);		return;	}	if (!tree[root].r)	{		int old=root;		root=tree[old].l;		tree[root].fa=0;		clear(old);		return;	}	int left=PRe(),old=root;	splay(left);	tree[tree[old].r].fa=root;	tree[root].r=tree[old].r;	clear(old);}

        pre和next操作分別作用是找某個數字x的前驅和后繼,即比該數大一個小一個的數字,實現該操作只需先把x插入樹中,再splay把x變為根,然后再找前驅和后繼,最后刪掉x即可。

inline int pre(){	int i=tree[root].l;						//根的左兒子一直往右就是前驅,后繼也是類似,就不貼上來了	while (tree[i].r) i=tree[i].r;	return i;}

        總的來說splay就是這幾大操作,真正應用起來多是會結合其他的算法,畢竟程序=數據結構+算法。然后要注意靈活運用就是了,還有Splay Tree 的復雜度雖然幾乎都是均攤的logn,但實際它的常數比較大,所以要注意。

        最后,我好像理解了,很多操作里的splay看似可有可無,但好像就是不斷的splay才能進行均攤,然后降低復雜度,好吧,我真的不會算復雜度……

        附上模板題:BZOJ 3224

Description

您需要寫一種數據結構(可參考題目標題),來維護一些數,其中需要提供以下操作:1. 插入x數2. 刪除x數(若有多個相同的數,因只刪除一個)3. 查詢x數的排名(若有多個相同的數,因輸出最小的排名)4. 查詢排名為x的數5. 求x的前驅(前驅定義為小于x,且最大的數)6. 求x的后繼(后繼定義為大于x,且最小的數)

Input

第一行為n,表示操作的個數,下面n行每行有兩個數opt和x,opt表示操作的序號(1<=opt<=6)

Output

對于操作3,4,5,6每行輸出一個數,表示對應答案

Sample Input

101 1064654 11 3177211 4609291 6449851 841851 898516 819681 4927375 493598

Sample Output

10646584185492737

HINT

1.n的數據范圍:n<=1000002.每個數的數據范圍:[-1e7,1e7]

代碼風格勿噴……

#include<iostream>#include<cstdio>#include<algorithm>#include<cstdlib>#include<cmath>#include<cstring>#include<iomanip>#define LL long long#define INF 2147483647#define ENT putchar('/n')#define up(_i,_a,_b) for (int _i=_a;_i<=_b;_i++)#define down(_i,_a,_b) for (int _i=_a;_i>=_b;_i--)#define efor(_i,_a) for (int _i=ls[_a];_i!=0;_i=g[_i].next)#define file(x) freopen(#x".in","r",stdin),freopen(#x".out","w",stdout);using namespace std;inline LL read(){         LL f=1,d=0;char s=getchar();         while (s<'0'||s>'9'){if (s=='-') f=-1;s=getchar();}         while (s>='0'&&s<='9'){d=d*10+s-'0';s=getchar();}         return f*d;}inline LL readln(){       LL f=1,d=0;char s=getchar();       while (s<'0'||s>'9'){if (s=='-') f=-1;s=getchar();}       while (s>='0'&&s<='9'){d=d*10+s-'0';s=getchar();}       while (s!='/n') s=getchar();       return f*d;}inline void write(LL x){    if(x==0){putchar('0');return;}if(x<0)putchar('-'),x=-x;    int len=0,buf[20];while(x)buf[len++]=x%10,x/=10;    int i=len-1; while (i>=0) {putchar(buf[i--]+'0');}return;}inline void writeln(LL x) {write(x);ENT;}struct node{	int l,r,fa,num,cnt,size;   //cnt為該關鍵字出現次數,size為子樹大小 } tree[100010];int root,sz;                  //sz為整棵樹的大小 inline void clear(int x){	tree[x].l=tree[x].r=0;	tree[x].fa=tree[x].num=0;	tree[x].cnt=tree[x].size=0;	} inline int get(int x)        //看x是左兒子還是右兒子 {	return tree[tree[x].fa].r==x;}inline void update(int x){	if (x)	{		tree[x].size=tree[x].cnt;		if (tree[x].l) tree[x].size+=tree[tree[x].l].size;		if (tree[x].r) tree[x].size+=tree[tree[x].r].size;	}}inline void rotate(int x){	int fa=tree[x].fa,grand=tree[fa].fa;	if (get(x))	{		tree[fa].r=tree[x].l;		tree[tree[fa].r].fa=fa;		tree[x].l=fa;	} else	{		tree[fa].l=tree[x].r;		tree[tree[fa].l].fa=fa;		tree[x].r=fa;	}	tree[x].fa=grand;	tree[fa].fa=x;	if (grand) 	{		if (tree[grand].r==fa) tree[grand].r=x;		                  else tree[grand].l=x;	}	update(fa); 	update(x);}inline void splay(int x){	for(;tree[x].fa;rotate(x));	root=x;}inline void insert(int x){	if (root==0)	{		clear(1);		tree[++sz].num=x;		tree[sz].size=tree[sz].cnt=1;		root=sz; return;	} 	int i=root,fa=0;	while (1)	{		if (tree[i].num==x)		{			tree[i].cnt++;			update(i);			update(fa);			splay(i);			return;		}		fa=i;		i=(x>tree[i].num?tree[i].r:tree[i].l);		if (i==0)		{			sz++; 			clear(sz);			tree[sz].num=x;			tree[sz].cnt=tree[sz].size=1;			tree[sz].fa=fa;			(x>tree[fa].num?tree[fa].r:tree[fa].l)=sz;			update(fa);			splay(sz);			return;		}	}}inline int find(int x){	int ans=0,i=root;	while (1)		if (x>=tree[i].num)		{			ans+=(tree[i].l?tree[tree[i].l].size:0);			if (x==tree[i].num) 			{				splay(i);				return ans+1;			}			ans+=tree[i].cnt;			i=tree[i].r;		} else i=tree[i].l;}inline int findx(int x){	int i=root;	while (1)		if ((tree[i].l)&&(x<=tree[tree[i].l].size)) i=tree[i].l;		else		{			int k=(tree[i].l?tree[tree[i].l].size:0)+tree[i].cnt;			if (x<=k) return tree[i].num;			i=tree[i].r; x-=k;		}}inline int pre(){	int i=tree[root].l;	while (tree[i].r) i=tree[i].r;	return i;}inline int next(){	int i=tree[root].r;	while (tree[i].l) i=tree[i].l;	return i;}inline void del(int x){	int locate=find(x);	if (tree[root].cnt>1) 	{		tree[root].cnt--;		return;	}	if ((!tree[root].l)&&(!tree[root].r))	{		clear(root);		root=0;		return;	}	if (!tree[root].l)	{		int old=root;		root=tree[old].r;		tree[root].fa=0;		clear(old);		return;	}	if (!tree[root].r)	{		int old=root;		root=tree[old].l;		tree[root].fa=0;		clear(old);		return;	}	int left=pre(),old=root;	splay(left);	tree[tree[old].r].fa=root;	tree[root].r=tree[old].r;	clear(old);	update(root);}int main(){	int n;	n=read();	up(i,1,n)	{		int opt,x;		opt=read();		x=read();		if (opt==1) insert(x);		if (opt==2) del(x);		if (opt==3) writeln(find(x));		if (opt==4) writeln(findx(x));		if (opt==5) 		{			insert(x);			find(x);			writeln(tree[pre()].num);			del(x);		}		if (opt==6)		{			insert(x);			find(x);			writeln(tree[next()].num);			del(x);		}	}}


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 亚洲免费高清 | 亚久久| 精品一区二区三区免费 | 成年人高清视频在线观看 | 美国一级黄色毛片 | 精品久久久久久久久久久久久久 | 一级毛片播放 | 色猫av | 国产成人综合在线观看 | 9999久久久久久 | 黄色免费在线电影 | 87成人免费看片 | 国产 日韩 一区 | 依依成人精品视频 | 久久国产精品免费视频 | 国内精品国产三级国产a久久 | 亚洲欧美一区二区三区在线观看 | hd极品free性xxx护士人 | 亚洲网站一区 | 黄色片网站免费看 | 黄色大片大毛片 | 亚洲精品7777xxxx青睐 | 久久国产精品99国产 | 一级网站| 嗯~啊~弄嗯~啊h高潮视频 | 第一区免费在线观看 | 亚洲第一色婷婷 | 成人偷拍片视频在线观看 | 欧美一区二区三区免费不卡 | 中文字幕在线免费看 | 亚洲精品aⅴ中文字幕乱码 欧美囗交 | 欧美性精品videofree | 最新91在线视频 | 午夜视频亚洲 | 久久精品一区二区三区国产主播 | 蜜桃视频在线观看免费 | 久久草在线观看视频 | 97精品国产高清在线看入口 | 久久国产精品久久久久久电车 | 国产91中文字幕 | 亚洲日本韩国在线观看 |