替罪羊樹是一類神奇的平衡樹。它最神奇的地方就在於,大部分平衡樹都是用愚蠢的單旋來維護平衡,而fhq-treap則是用split和merge維護平衡的,可替罪羊樹卻是用一種神奇的操作維護平衡的,那就是重構rebuild。每次插入和刪除元素的時候,檢查子樹大小,若失衡則直接重購以維護整棵樹平衡。
在講思路前,我們先要注意乙個點:替罪羊樹僅能用重構來維持平衡,因而若節點被刪除後,無法將其高效的移除,所以我們採取懶惰刪除,每次僅將節點的計數器--即可。
先來看一下我們的變數區和基礎的函式吧:
struct node;
inline void update(int p)
接下來我們來看看重構操作吧。我們預先設定乙個閾值α,一般取[0.7,0.8]左右,若檢查到某個節點的兒子所在子樹的sz佔這個節點sz的比例超過α時,就重構。由於當被刪除節點過多時,搜尋樹效率也會顯著降低,於是當乙個節點的sd佔其sz的比例超過α時,亦重構。inline bool can_rbu(int p)
那麼怎麼重構呢?我們已經知道了,對於一棵平衡樹,它的中序遍歷是排好序的。所以我們把它的中序遍歷拉到陣列中,然後重新二分建樹即可:int tmp[100010];
void rbu_flatten(int &num,int p)
int rbu_build(int l,int r)
void rbu(int &p)
以上就是基本函式啦!其餘的部分神似普通的平衡樹,我們的介紹和注釋就稍微簡略一些了咯。
首先是插入和刪除。我們在遍歷路徑上每乙個節點時,都檢查是否滿足重構條件,並重構即可。其餘與普通二叉搜尋時雷同。
void insert(int &p,int val)
else
最後是查詢前後驅的函式。在此我們稍微的改變一下函式的返回值:將返回數值改為返回下標。這樣以後查詢排名就可以用前驅座標+1即可。**依舊與普通平衡樹相同……int get_uprb(int p,int val)
while (isdigit(ch))
x *= f;
return;
}template void write(t x)
if(x > 9)
write(x/10);
putchar(x % 10 + '0');
return;
}int n;
int rt,tot=0;
struct nodef[100010];
const double alpha=0.75;
inline void update(int p)
inline bool can_rbu(int p)
int tmp[100010];
void rbu_flatten(int &num,int p)
int rbu_build(int l,int r)
void rbu(int &p)
void insert(int &p,int val)
else
int main()
} return 0;
}
替罪羊樹SCT
照著指標版的打了個陣列版的,發現自己上了一節c 語法課。重構什麼的,最難在於找出深度最小的需要重構的節點,畢竟多寫乙個函式不太優雅,陣列又不像指標那麼方便直接修改。如果你要傳回乙個變數的位址 因為你直接修改這個位址ch等等就直接跟著改了,十分方便 函式不能寫 int 得寫int 刪除還是不要重構,太...
總結 替罪羊樹學習筆記
突然不會寫學習筆記了.反正是給自己看的 那就想到哪寫到哪吧 不基於旋轉而基於暴力重構的平衡樹 採用懶惰刪除法 每次刪除只打標記,在重構時統一刪除 如果某個點的子樹過於不平衡或刪除了過多的元素 那就需要進行重構 重構時暴力dfs整棵子樹,碰見打了刪除標記的節點就扔進記憶體池,否則按照中序遍歷存下來,然...
平衡樹 替罪羊樹
yangkai 身為平衡樹卻不做任何形式的旋轉,替罪羊樹可以稱得上是最暴力的平衡樹了。替罪羊樹 sgt 保留有二叉搜尋樹的基本性質,即對於任意乙個節點t,左兒子的所有節點比它小,右兒子的所有節點比它大。但是既然不基於翻轉,它怎樣維護平衡樹的優秀複雜度呢?sdt基於乙個叫做 重構 的操作,聽起來很是優...