【省選】演算法總結——平衡樹
130409總結——平衡樹
其實到現在為止Treap還沒寫成功過(網上是指標,就照著指標寫。。。。球了唄。。。)
不過後來還好,SBT是陣列的,理解後想改成指標(看起來多好看的),是在無力啊。。。
想想還是算了,就陣列吧,不容易出錯
1.二叉查詢樹(二叉搜尋樹)BST
1.1基本思想
其實思想很簡單,就是建立一顆二叉樹,左子樹都小於根,右子樹都大於根,來實現快速查詢(其實很慢。。。)
1.2插入
插入X,如果X<當前節點的值(key),就把X插入左子樹,否則插入右子樹(等於也在右子樹,方便且不容易出錯)
1.3查詢
如果要查詢一個數X,如果X<當前key,就在左子樹找,否則就在當前節點或者右子樹找
弊端
前面說這個似快非快是有道理的,比如一個遞增序列,X1插入跟,X2進入右子樹,X3進入右子樹的右子樹,。。。。,這樣這棵樹就退化成一條鏈了!
所以我們需要實現平衡!實現嚴格log2N!
2.隨機二叉查詢樹Treap
顧名思義,Treap= Tree + heap,所以也稱作“樹堆”
前面說了BST退化的原因在於樹的根固定了,兒子有可能一直往一邊跑,那我們可不可以通過變換把樹平衡呢?先看下圖
我們可以通過把2或3給dia起來實現樹的平衡,如下圖
而Treap的思想就是給每個節點多維護一個附加資訊fix,給它一個隨機值,然後在保證key為二叉查詢樹的基礎上再給fix建立一個小(大)根堆,由於隨機數是大致平均的,所以我們也可以實現大致的平衡,雖然不是嚴格log2
操作都差不多。。。。。
3.SizeBalanced Tree
3.1基本思想
和Treap差不多,都是想辦法讓樹平衡,不過Treap額外維護了一個沒用的fix,並且還要靠RP,萬一臨考前RP不好就球了
而SBT的核心思想就是Maintain函式,它不借助額外的資訊,我們說樹不平衡就是說某個子樹的節點數(size)多了,所以我們就可以藉助這個size來使這個樹左右平衡!
3.2儲存結構
可以開結構體,但是寫出來程式碼很長(後面題目裡面有一份是結構體的,很蛋疼。。。。)
所以還是開陣列方便些
int S[N],L[N],R[N],A[N];//分別表示size,左兒子,右兒子,和當前key
3.3Maintain函式
為了描述方便用S[]表示size,L[]表示左子樹,R[]表示右子樹
Maintain分兩類
-
調整右子樹,就比較S[R[R[p]]]和S[L[p]],如果前者大,就把p左旋,樹就平衡了;否則就再比較S[R[L[p]]]和S[L[p]],如果前者大,就先把R[p]右旋,再把p左旋,整棵樹就平衡了;否則return
-
調整左子樹就與上面完全相反
通過上面的分析我們可以得知,核心思想就是把Size大的儘量向上走
void maintain(int &p,bool flag) //flag為true表示調整右子樹
{
if(flag)
{
if(S[R[R[p]]]>S[L[p]]) rotate_left(p);
else if(S[R[L[p]]]>S[L[p]])
{
rotate_right(R[p]);
rotate_left(p);
}
else return;
}
else{
if(S[L[L[p]]]>S[R[p]]) rotate_right(p);
else if(S[L[R[p]]]>S[R[p]])
{
rotate_left(L[p]);
rotate_right(p);
}
else return;
}
maintain(L[p],0);
maintain(R[p],1);
maintain(p,1);
maintain(p,0);
}
核心模組完了其他就好寫了
3.4左旋右旋
void rotate_left(int &x)
{
int y=R[x];
R[x]=L[y];
L[y]=x;
S[y]=S[x];
S[x]=S[L[x]]+S[R[x]]+1;
x=y;
}
void rotate_right(int &x)
{
int y=L[x];
L[x]=R[y];
R[y]=x;
S[y]=S[x];
S[x]=S[L[x]]+S[R[x]]+1;
x=y;
}
3.5插入
void insert(int &p,int k)
{
if(!p) {p=++total;init(p,k);}
else{
S[p]++;
if(k<A[p]) insert(L[p],k);
else insert(R[p],k);
maintain(p,k>=A[p]);
}
}
3.6第K大(整個區間)
int kth(int &p,int k)
{
int tmp=S[L[p]]+1;
if(k==tmp) return A[p];
else if(k<tmp) return kth(L[p],k);
else return kth(R[p],k-tmp);
}
3.7找最值
顯然,最值肯定出現在整棵樹最左(右)
int getmin()
{
int x;
for(x=root ;L[x];x= L[x]);
return A[x];
}
int getmax()
{
int x;
for(x=root ;R[x];x=R[x]);
return A[x];
}
3.8刪除
刪除操作要是題目要求而寫的很不同,所以就不寫了
差不多就這些了。。。
目前只做過一道SBT題目(儲存方式結構體和陣列都有)