替罪羊樹是一種優雅的暴力,它通過設立
替罪羊樹是一棵平衡二叉樹,但是眾所周知,如果我們按照一棵二叉搜尋樹bst的道理來直接插入或者刪除點的話,很容易使得它不夠平衡,變得瘦瘦高高的,我們不喜歡這種瘦瘦高高,更喜歡平攤高度的矮矮胖胖。如何平攤高度,這是替罪羊樹要處理的東西。
有多暴力?直接把這棵子樹拉出來,利用
什麼時候是不平衡的,我們定義二叉樹有左右子結點,如果說左右子樹的有效點的權重任一存在大於了目前子樹的有效點的權重乘以
什麼點是有效的?自然是存在的點,那為什麼會有無效的點?因為刪除操作,我們不能直接把點刪除掉,因為會影響到它的後繼點的繼承,所以我們會偷懶,如何偷懶?直接給它打上刪除標記,然後不管它了。所以,子樹中可能存在這樣的名存實亡的無效點。
那麼,如果無效點多了,那麼如果搜尋的時候碰到了很多的無效點,自然浪費的很!所以當無效點大於了有效點的30%或者40%的時候,我們可以再把這棵子樹拉出來重建,因為重建是只對有效點的重建。
大致上我們已經捋清了整體的框架,接下去就是講一些細節上的,**上的操作了。
void add(int x)
int p = fid(x, root);
if(x == tree[p].x)
else updata(p, 0, 0, 1);
}else if(x < tree[p].x)
else
find_rebuild(root, x);
}
如果現在是一棵空樹,那麼root不存在,此時直接給開個新樹。
否則,我們需要找到值x需要插入的位置,標明它需要插在哪個結點的下面,這是基礎的二叉搜尋樹的操作。
void build(int x,int y,int fa)
int fid(int x, int now)
void del(int x)
else updata(p, 0, 0, -1);
find_rebuild(root, x);
}
我們會發現,插入和刪除兩個操作裡面都有乙個神奇的find_rebuild,這到底是個什麼玩意兒呢?
void find_rebuild(int now,int x)
if(tree[now].x ^ x) find_rebuild(x < tree[now].x ? tree[now].lc : tree[now].rc, x);
}
這個就是我們上面說到的重建的規則,如果子樹的有效點的數量大於了總數的數量乘以
void rebuild(int x)
}
int readd(int l,int r,int fa)
void dfs_rebuild(int x)
ck[++t] = x;
dfs_rebuild(tree[x].rc);
}
每次刪除點或者是加入點,都會影響到根到該點鏈上的所有的點,我們還需要更新他們的值。
void updata(int x, int y, int z, int k)
inline int kk()
#include #include #include #include #include #include #include #include #include #include #include #include #include //#include //#include #define lowbit(x) ( x&(-x) )
#define pi 3.141592653589793
#define e 2.718281828459045
#define inf 0x3f3f3f3f
#define eps 1e-8
#define half (l + r)>>1
#define lsn rt<<1
#define rsn rt<<1|1
#define lson lsn, l, mid
#define rson rsn, mid+1, r
#define ql lson, ql, qr
#define qr rson, ql, qr
#define myself rt, l, r
#define mp(a, b) make_pair(a, b)
using namespace std;
typedef unsigned long long ull;
typedef unsigned int uit;
typedef long long ll;
const int maxn = 1e5 + 7;
const double alpha = 0.75;
struct node
} tree[maxn];
int len = 0, n, root = 0;
int ck[maxn], t = 0;
void build(int x,int y,int fa)
inline int kk()
void updata(int x, int y, int z, int k)
int fid(int x, int now)
struct sl
} shulie[maxn];
int tt;
void dfs_rebuild(int x)
ck[++t] = x;
dfs_rebuild(tree[x].rc);
}int readd(int l,int r,int fa)
void rebuild(int x)
}void find_rebuild(int now,int x)
if(tree[now].x ^ x) find_rebuild(x < tree[now].x ? tree[now].lc : tree[now].rc, x);
}void add(int x)
int p = fid(x, root);
if(x == tree[p].x)
else updata(p, 0, 0, 1);
}else if(x < tree[p].x)
else
find_rebuild(root, x);
}void del(int x)
else updata(p, 0, 0, -1);
find_rebuild(root, x);
}int fid_rank(int x)
}ans += tree[tree[now].lc].whsize;
return ans + 1;
}void rank_to_x(int kth)
else
}}int main()
return 0;
}
平衡樹 替罪羊樹
yangkai 身為平衡樹卻不做任何形式的旋轉,替罪羊樹可以稱得上是最暴力的平衡樹了。替罪羊樹 sgt 保留有二叉搜尋樹的基本性質,即對於任意乙個節點t,左兒子的所有節點比它小,右兒子的所有節點比它大。但是既然不基於翻轉,它怎樣維護平衡樹的優秀複雜度呢?sdt基於乙個叫做 重構 的操作,聽起來很是優...
替罪羊樹模版 普通平衡樹
替罪羊樹,賊長,我哭了。include include include include include using namespace std const int maxn 1e5 5 const double alpha 0.75 struct nodetzy maxn intcnt,root 新...
初涉平衡樹 替罪羊樹
替罪羊樹 乙個看上去很玄學的名字 替罪羊 這個名字非常有趣 以至於一開始我並不覺得這是什麼好懂的東西 名字的 大概是由於它在刪除時候需要用被刪除節點的 左子樹最後乙個節點 右子樹第乙個節點來頂替這個節點。資料結構圈居然還有這麼腦洞的名字 好像還有乙個東西叫做朝鮮樹來著?替罪羊的精華在於,它相較於其他...