因為伸展樹可以分裂合併,所以可以取代分裂合併式treap或是分裂合併式randomize binary tree的功能,可以在競賽中被使用,而普通的伸展樹效率不高,故本文只介紹作區間處理的伸展樹
之後的link-cut tree會用到他,所以必須要能理解
例題: UVA 11922
定義伸展樹節點:(T為資料型態)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
struct node{ | |
T data; | |
node *ch[2]; | |
int s; | |
node(const T&d):data(d), s(1){} | |
node():s(0){} | |
/*cmp是很重要的一個函式一定要理解*/ | |
char cmp(int v){ | |
int d=v-ch[0]->s; | |
if(d==1)return -1; | |
return d>0; | |
} | |
void down(){ | |
/*可以加一些下推懶惰標記*/ | |
} | |
void up(){ | |
s=ch[0]->s+ch[1]->s+1; | |
/*可以加一些上推懶惰標記*/ | |
} | |
}*root,*nil; |
初始化時nil的l,r要指向自己
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
nil=new node(); | |
root=nil->ch[0]=nil->ch[1]=nil; |
旋轉(跟其他樹差不多)
d=0左旋,1右旋
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
void rotate(node *&a,bool d){ | |
node *b=a; | |
a=a->ch[!d]; | |
b->ch[!d]=a->ch[d]; | |
a->ch[d]=b; | |
b->up(); | |
a->up(); | |
} |
接下來是最重要的伸展操作
這是按劉汝佳在橘色那本書的寫法實現的
主要是將第k個節點伸展到root的過程
注意!! k必須大於0
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
void splay(node *&o,int k){ | |
o->down(); | |
char d1=o->cmp(k); | |
if(~d1){ | |
if(d1)k-=o->ch[0]->s+1; | |
node *p=o->ch[d1]; | |
p->down(); | |
char d2=p->cmp(k); | |
if(~d2){ | |
int k2=d2?k-p->ch[0]->s-1:k; | |
splay(p->ch[d2],k2); | |
if(d1==d2)rotate(o,d1^1); | |
else rotate(o->ch[d1],d1); | |
} | |
rotate(o,d1^1); | |
} | |
} |
有了splay操作,接下來是分裂&合併
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* l不能為nil */ | |
node *merge(node *l,node *r){ | |
splay(l,l->s); | |
l->ch[1]=r; | |
l->up(); | |
return l; | |
} | |
/* k必須 > 0 */ | |
void split(node *o,node *&l,node *&r,int k){ | |
splay(o,k); | |
l=o; | |
r=o->ch[1]; | |
o->ch[1]=nil; | |
l->up(); | |
} |
當我們要維護一個區間1~n時,可以建立0~n的splay tree,0是廢棄節點,較為方便使用
最後提供新增節點的方法:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
inline node *new_node(const T&d){ | |
node *o=new node(d); | |
o->l=o->r=nil; | |
} |
這樣所有的操作就齊全了
剩下的一些懶惰標記跟treap一樣,可以做區間翻轉和線段樹的功能
但是請小心指標的使用
(nil為衛兵指標,方便用來編輯,若不想用可以將程式碼略為修改)
沒有留言:
張貼留言