Treap樹
核心是 利用隨機數的二叉排序樹的各種操作復雜度平均為O(lgn)
Treap模板:
#include <cstdio> #include <cstring> #include <ctime> #include <iostream> #include <algorithm> #include <cstdlib> #include <cmath> #include <utility> #include <vector> #include <queue> #include <map> #include <set> #define max(x,y) ((x)>(y)?(x):(y)) #define min(x,y) ((x)>(y)?(y):(x)) #define INF 0x3f3f3f3f #define MAXN 100005 using namespace std; int cnt=1,rt=0; //節點編號從1開始 struct Tree { int key, size, pri, son[2]; //保證父親的pri大於兒子的pri void set(int x, int y, int z) { key=x; pri=y; size=z; son[0]=son[1]=0; } }T[MAXN]; void rotate(int p, int &x) { int y=T[x].son[!p]; T[x].size=T[x].size-T[y].size+T[T[y].son[p]].size; T[x].son[!p]=T[y].son[p]; T[y].size=T[y].size-T[T[y].son[p]].size+T[x].size; T[y].son[p]=x; x=y; } void ins(int key, int &x) { if(x == 0) T[x = cnt++].set(key, rand(), 1); else { T[x].size++; int p=key < T[x].key; ins(key, T[x].son[!p]); if(T[x].pri < T[T[x].son[!p]].pri) rotate(p, x); } } void del(int key, int &x) //刪除值為key的節點 { if(T[x].key == key) { if(T[x].son[0] && T[x].son[1]) { int p=T[T[x].son[0]].pri > T[T[x].son[1]].pri; rotate(p, x); del(key, T[x].son[p]); } else { if(!T[x].son[0]) x=T[x].son[1]; else x=T[x].son[0]; } } else { T[x].size--; int p=T[x].key > key; del(key, T[x].son[!p]); } } int find(int p, int &x) //找出第p小的節點的編號 { if(p == T[T[x].son[0]].size+1) return x; if(p > T[T[x].son[0]].size+1) find(p-T[T[x].son[0]].size-1, T[x].son[1]); else find(p, T[x].son[0]); } int find_NoLarger(int key, int &x) //找出值小於等於key的節點個數 { if(x == 0) return 0; if(T[x].key <= key) return T[T[x].son[0]].size+1+find_NoLarger(key, T[x].son[1]); else return find_NoLarger(key, T[x].son[0]); }
相關題解:
Splay Tree(伸展樹)
核心就是 過程Splay(x, y),即將x節點轉移到y節點的子節點上面(其中y是x的祖先)。
利用其中雙旋的優勢能夠保證查詢復雜度均攤為O(lgn)
一開始理解有些困難,其實實際上不做深入的理解就是,雙旋的過程就是一個建立相對平衡的二叉樹的一個過程。
》對於二叉樹,最極端的情況就是線性插入,使得整棵二叉樹退化為一條鏈。比如你查詢鏈的最后一個節點,之后再次查詢第一個節點。
1)若只是單旋通過Splay(x, 0)將最后一個節點移動到根節點,需要O(n)復雜度,而查詢第一個節點時又需要O(n)復雜度,來來往往就退化成一條鏈了。
2)若是雙旋Splay(x, 0)將最后一個節點移動到根節點上時,移動過程中建立起了相對平衡的二叉樹,需要O(n),也就是查詢第一個節點時,大概是需要O(lgn)復雜度。這就降低了復雜度。可以證明,總的每個操作的均攤復雜度是O(lgn)。
具體證明可以參見 楊思雨《伸展樹的基本操作與應用》
I 用於維護單調隊列:(以key為維護對象保證單調)
常用版:(支持相同值)
Struct Tree{
int key, size, fa, son[2];
}
void PushUp(int x);
void Rotate(int x, int p); //0左旋 1右旋
void Splay(int x, int To) //將x節點插入到To的子節點中
int find(int key) //返回值為key的節點 若無返回0 若有將其轉移到根處
int prev() //返回比根值小的最大值 若無返回0 若有將其轉移到根處
int succ() //返回比根值大的最小值 若無返回0 若有將其轉移到根處
void Insert(int key) //插入key 並且將該節點轉移到根處
void Delete(int key) //刪除值為key的節點 若有重點只刪其中一個 x的前驅移動到根處
int GetPth(int p) //獲得第p小的節點 並將其轉移到根處
int GetRank(int key) //獲得值<=key的節點個數 並將其轉移到根處 若<key只需將<=換為<

int cnt=1, rt=0; struct Tree { int key, size, fa, son[2]; void set(int _key, int _size, int _fa) { key=_key; size=_size; fa=_fa; son[0]=son[1]=0; } }T[MAXN]; inline void PushUp(int x) { T[x].size=T[T[x].son[0]].size+T[T[x].son[1]].size+1; } inline void Rotate(int x, int p) //0左旋 1右旋 { int y=T[x].fa; T[y].son[!p]=T[x].son[p]; T[T[x].son[p]].fa=y; T[x].fa=T[y].fa; if(T[x].fa) T[T[x].fa].son[T[T[x].fa].son[1] == y]=x; T[x].son[p]=y; T[y].fa=x; PushUp(y); PushUp(x); } void Splay(int x, int To) //將x節點插入到To的子節點中 { while(T[x].fa != To) { if(T[T[x].fa].fa == To) Rotate(x, T[T[x].fa].son[0] == x); else { int y=T[x].fa, z=T[y].fa; int p=(T[z].son[0] == y); if(T[y].son[p] == x) Rotate(x, !p), Rotate(x, p); //之字旋 else Rotate(y, p), Rotate(x, p); //一字旋 } } if(To == 0) rt=x; } int find(int key) //返回值為key的節點 若無返回0 若有將其轉移到根處 { int x=rt; while(x && T[x].key != key) x=T[x].son[key > T[x].key]; if(x) Splay(x, 0); return x; } int prev() //返回比根值小的最大值 若無返回0 若有將其轉移到根處 { int x=T[rt].son[0]; if(!x) return 0; while(T[x].son[1]) x=T[x].son[1]; Splay(x, 0); return x; } int succ() //返回比根值大的最小值 若無返回0 若有將其轉移到根處 { int x=T[rt].son[1]; if(!x) return 0; while(T[x].son[0]) x=T[x].son[0]; Splay(x, 0); return x; } void Insert(int key) //插入key 並且將該節點轉移到根處 { if(!rt) T[rt = cnt++].set(key, 1, 0); else { int x=rt, y=0; while(x) { y=x; x=T[x].son[key > T[x].key]; } T[x = cnt++].set(key, 1, y); T[y].son[key > T[y].key]=x; Splay(x, 0); } } void Delete(int key) //刪除值為key的節點 若有重點只刪其中一個 x的前驅移動到根處 { int x=find(key); if(!x) return; int y=T[x].son[0]; while(T[y].son[1]) y=T[y].son[1]; int z=T[x].son[1]; while(T[z].son[0]) z=T[z].son[0]; if(!y && !z) { rt=0; return; } if(!y) { Splay(z, 0); T[z].son[0]=0; PushUp(z); return; } if(!z) { Splay(y, 0); T[y].son[1]=0; PushUp(y); return; } Splay(y, 0); Splay(z, y); T[z].son[0]=0; PushUp(z); PushUp(y); } int GetPth(int p) //獲得第p小的節點 並將其轉移到根處 { if(!rt) return 0; int x=rt, ret=0; while(x) { if(p == T[T[x].son[0]].size+1) break; if(p>T[T[x].son[0]].size+1) { p-=T[T[x].son[0]].size+1; x=T[x].son[1]; } else x=T[x].son[0]; } Splay(x, 0); return x; } int GetRank(int key) //獲得值<=key的節點個數 並將其轉移到根處 若<key只需將<=換為< { if(!rt) return 0; int x=rt, ret=0, y; while(x) { y=x; if(T[x].key <= key) { ret+=T[T[x].son[0]].size+1; x=T[x].son[1]; } else x=T[x].son[0]; } Splay(y, 0); return ret; }
完全版:(支持相同值,支持區間刪除,支持懶惰標記)
Struct Tree{
int key, num, size, fa, son[2];
}
void PushUp(int x);
void PushDown(int x);
int Newnode(int key, int fa); //新建一個節點並返回
void Rotate(int x, int p); //0左旋 1右旋
void Splay(int x, int To); //將x節點移動到To的子節點中
int GetPth(int p, int To); //返回第p小的節點 並移動到To的子節點中
int Find(int key); //返回值為key的節點 若無返回0 若有將其轉移到根處
int Prev(); //返回根節點的前驅
int Succ(); //返回根結點的后繼
void Insert(int key); //插入key值
void Delete(int key); //刪除值為key的節點
int GetRank(int key); //獲得值<=key的節點個數
void Delete(int l, int r); //刪除值在[l, r]中的節點

int cnt, rt; int Add[MAXN]; struct Tree{ int key, num, size, fa, son[2]; }T[MAXN]; inline void PushUp(int x) { T[x].size=T[T[x].son[0]].size+T[T[x].son[1]].size+T[x].num; } inline void PushDown(int x) { if(Add[x]) { if(T[x].son[0]) { T[T[x].son[0]].key+=Add[x]; Add[T[x].son[0]]+=Add[x]; } if(T[x].son[1]) { T[T[x].son[1]].key+=Add[x]; Add[T[x].son[1]]+=Add[x]; } Add[x]=0; } } inline int Newnode(int key, int fa) //新建一個節點並返回 { ++cnt; T[cnt].key=key; T[cnt].num=T[cnt].size=1; T[cnt].fa=fa; T[cnt].son[0]=T[cnt].son[1]=0; return cnt; } inline void Rotate(int x, int p) //0左旋 1右旋 { int y=T[x].fa; PushDown(y); PushDown(x); T[y].son[!p]=T[x].son[p]; T[T[x].son[p]].fa=y; T[x].fa=T[y].fa; if(T[x].fa) T[T[x].fa].son[T[T[x].fa].son[1] == y]=x; T[x].son[p]=y; T[y].fa=x; PushUp(y); PushUp(x); } void Splay(int x, int To) //將x節點移動到To的子節點中 { while(T[x].fa != To) { if(T[T[x].fa].fa == To) Rotate(x, T[T[x].fa].son[0] == x); else { int y=T[x].fa, z=T[y].fa; int p=(T[z].son[0] == y); if(T[y].son[p] == x) Rotate(x, !p), Rotate(x, p); //之字旋 else Rotate(y, p), Rotate(x, p); //一字旋 } } if(To == 0) rt=x; } int GetPth(int p, int To) //返回第p小的節點 並移動到To的子節點中 { if(!rt || p > T[rt].size) return 0; int x=rt; while(x) { PushDown(x); if(p >= T[T[x].son[0]].size+1 && p <= T[T[x].son[0]].size+T[x].num) break; if(p > T[T[x].son[0]].size+T[x].num) { p-=T[T[x].son[0]].size+T[x].num; x=T[x].son[1]; } else x=T[x].son[0]; } Splay(x, 0); return x; } int Find(int key) //返回值為key的節點 若無返回0 若有將其轉移到根處 { if(!rt) return 0; int x=rt; while(x) { PushDown(x); if(T[x].key == key) break; x=T[x].son[key > T[x].key]; } if(x) Splay(x, 0); return x; } int Prev() //返回根節點的前驅 非重點 { if(!rt || !T[rt].son[0]) return 0; int x=T[rt].son[0]; while(T[x].son[1]) { PushDown(x); x=T[x].son[1]; } Splay(x, 0); return x; } int Succ() //返回根結點的后繼 非重點 { if(!rt || !T[rt].son[1]) return 0; int x=T[rt].son[1]; while(T[x].son[0]) { PushDown(x); x=T[x].son[0]; } Splay(x, 0); return x; } void Insert(int key) //插入key值 { if(!rt) rt=Newnode(key, 0); else { int x=rt, y=0; while(x) { PushDown(x); y=x; if(T[x].key == key) { T[x].num++; T[x].size++; break; } T[x].size++; x=T[x].son[key > T[x].key]; } if(!x) x=T[y].son[key > T[y].key]=Newnode(key, y); Splay(x, 0); } } void Delete(int key) //刪除值為key的節點1個 { int x=Find(key); if(!x) return; if(T[x].num>1) { T[x].num--; PushUp(x); return; } int y=T[x].son[0]; while(T[y].son[1]) y=T[y].son[1]; int z=T[x].son[1]; while(T[z].son[0]) z=T[z].son[0]; if(!y && !z) { rt=0; return; } if(!y) { Splay(z, 0); T[z].son[0]=0; PushUp(z); return; } if(!z) { Splay(y, 0); T[y].son[1]=0; PushUp(y); return; } Splay(y, 0); Splay(z, y); T[z].son[0]=0; PushUp(z); PushUp(y); } int GetRank(int key) //獲得值<=key的節點個數 { if(!Find(key)) { Insert(key); int tmp=T[T[rt].son[0]].size; Delete(key); return tmp; } else return T[T[rt].son[0]].size+T[rt].num; } void Delete(int l, int r) //刪除值在[l, r]中的所有節點 l!=r { if(!Find(l)) Insert(l); int p=Prev(); if(!Find(r)) Insert(r); int q=Succ(); if(!p && !q) { rt=0; return; } if(!p) { T[rt].son[0]=0; PushUp(rt); return; } if(!q) { Splay(p, 0); T[rt].son[1]=0; PushUp(rt); return; } Splay(p, q); T[p].son[1]=0; PushUp(p); PushUp(q); }
速度相對來說都還不錯,POJ這些都3~500ms,郁悶的出納員900多ms
相關題解:
II 用於維護序列:(以序列下標為對象維護,相當於對區間操作)(能夠完成線段樹的操作及其不能完成的操作)
Struct Tree{
int key, sum, size, fa, son[2];
}
支持操作:
void PushUp(int x);
void PushDown(int x);
int MakeTree(int l, int r, int a[]); //新建一個子樹返回根節點
void Rotate(int x, int p); //0左旋 1右旋
void Splay(int x, int To); //將x節點移動到To的子節點中
int Select(int p, int To); //將第p個數移動到To的子節點中 並返回該節點
int Find(int key); //返回值為key的節點 若無返回0 若有將其轉移到根處
int Prev(); //返回根節點的前驅
int Succ(); //返回根結點的后繼
void Insert(int p, int l, int r, int a[]) //將a[l .. r]的數插入到下標為p后面
void Delete(int l, int r); //刪除區間[l, r]中的節點
int Query(int l, int r); //返回[l, r]的和
待補充。。
Size Balance Tree
和上述兩種二叉樹比起來,SBT可能是最像真正平衡二叉樹吧。
SBT能夠保證樹的高度在lgn,這樣對於插入,刪除操作都能夠准確保證時間復雜度在O(lgn)
Maintain操作事實上理解起來也是挺簡單的,至於證明參見CQF神牛的《SBT》
int cnt, rt; struct Tree { int key, size, son[2]; }T[MAXN]; inline void PushUp(int x) { T[x].size=T[T[x].son[0]].size+T[T[x].son[1]].size+1; } inline int Newnode(int key) { ++cnt; T[cnt].key=key; T[cnt].size=1; T[cnt].son[0]=T[cnt].son[1]=0; return cnt; } void Rotate(int p, int &x) { int y=T[x].son[!p]; T[x].son[!p]=T[y].son[p]; T[y].son[p]=x; PushUp(x); PushUp(y); x=y; } void Maintain(int &x, int p) //維護SBT的!p子樹 { if(T[T[T[x].son[p]].son[p]].size > T[T[x].son[!p]].size) Rotate(!p, x); else if(T[T[T[x].son[p]].son[!p]].size > T[T[x].son[!p]].size) Rotate(p, T[x].son[p]), Rotate(!p, x); else return; Maintain(T[x].son[0], 0); Maintain(T[x].son[1], 1); Maintain(x, 0); Maintain(x, 1); } inline int Prev() //返回比根值小的最大值 若無返回0 { int x=T[rt].son[0]; if(!x) return 0; while(T[x].son[1]) x=T[x].son[1]; return x; } inline int Succ() //返回比根值大的最小值 若無返回0 { int x=T[rt].son[1]; if(!x) return 0; while(T[x].son[0]) x=T[x].son[0]; return x; } void Insert(int key, int &x) { if(!x) x=Newnode(key); else { T[x].size++; Insert(key, T[x].son[key > T[x].key]); Maintain(x, key > T[x].key); } } bool Delete(int key, int &x) //刪除值為key的節點 key可以不存在 { if(!x) return 0; if(T[x].key == key) { if(!T[x].son[0]) { x=T[x].son[1]; return 1; } if(!T[x].son[1]) { x=T[x].son[0]; return 1; } int y=Prev(); T[x].size--; return Delete(T[x].key, T[x].son[0]); } else if(Delete(key, T[x].son[key > T[x].key])) { T[x].size--; return 1; } } int GetPth(int p, int &x) //返回第p小的節點 { if(!x) return 0; if(p == T[T[x].son[0]].size+1) return x; if(p > T[T[x].son[0]].size+1) return GetPth(p-T[T[x].son[0]].size-1, T[x].son[1]); else return GetPth(p, T[x].son[0]); } int GetRank(int key, int &x) //找出值<=key的節點個數 { if(!x) return 0; if(T[x].key <= key) return T[T[x].son[0]].size+1+GetRank(key, T[x].son[1]); else return GetRank(key, T[x].son[0]); }
相關題解:
上述題均為用於測試平衡樹基本操作的題目。
提高題:(暫時未寫)